Sometimes an Alfresco Administrator might need to track active logged-in users in the system for audit purpose or for planning maintenance activities. There can be many other use-cases depending of type of usage or organization policies.
Alfresco doesn't provide this kind of feature for admins out of the box as of now. However Alfresco 5.2 version on-wards they are providing Support Tools feature which can provide various options including this particular use-case.
Support Tools is an Add-On, which is available on GitHub as alfresco-support-tools which can be installed as a module as well. Alfresco 5.2 (enterprise) on-wards alfresco has integrated this add-on out of the box.
See here: Support Tools
Here we are going to use Alfresco's TicketComponent service to get the active user details and active ticket details.
We will create a java backed web-script which will return the basic details about the active users, total active user count, total no. of active tickets etc.
Follow the below given steps:
- Create following folder structure under "/alfresco/extension/templates/webscripts" directory
- Create a folder named "com" under "webscripts"
- Under folder "com", create a folder named "github"
- Under folder "github", create a folder named "abhinavmishra14"
- Under folder "abhinavmishra14", create a folder named "audit"
- Create the alfresco webscript descriptor “getActiveUsers.get.desc.xml” under "com > github > abhinavmishra14 > audit" folder.
<webscript> <shortname>Active Users</shortname> <description>This webscript returns the active users logged-in into Alfresco. Sample response: { activeTicketsCount: 2, activeUsers: "["admin","abhinav@gmail.com "]", activeUsersCount: 2, _comment: "Active user count may be lower than the ticket count, since a user can have more than one ticket/session. Ticket count may be higher than the active user count, since a user can have more than one ticket/session." } </description> <url>/audit/getActiveUsers</url> <format default="json" /> <authentication>admin</authentication> <family>Audit</family> </webscript>
- Create a freemarker template “getActiveUsers.get.json.ftl” which is used to generate the view under "com > github > abhinavmishra14 > audit" folder.
<#escape x as jsonUtils.encodeJSONString(x)> ${response} </#escape>
- Create a java package "com.github.abhinavmishra14.audit.webscript"
- Create java class "GetActiveUsersWebscript" under the "com.github.abhinavmishra14.audit.webscript" package.
/* * Created By: Abhinav Kumar Mishra * Copyright © 2017. Abhinav Kumar Mishra. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.abhinavmishra14.audit.webscript; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.security.authentication.TicketComponent; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.extensions.webscripts.Cache; import org.springframework.extensions.webscripts.DeclarativeWebScript; import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; /** * The Class GetActiveUsersWebscript. */ public class GetActiveUsersWebscript extends DeclarativeWebScript { /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(GetActiveUsersWebscript.class); /** The Constant ACTIVE_USERS. */ private static final String ACTIVE_USERS = "activeUsers"; /** The Constant ACTIVE_USERS_COUNT. */ private static final String ACTIVE_USERS_COUNT = "activeUsersCount"; /** The Constant ACTIVE_TICKETS_COUNT. */ private static final String ACTIVE_TICKETS_COUNT = "activeTicketsCount"; /** The Constant COMMENT_DATA. */ private static final String COMMENT_DATA = "_comment"; /** The Constant RESPONSE. */ private static final String RESPONSE = "response"; /** The ticket component. */ private final TicketComponent ticketComponent; /** * The Constructor. * * @param ticketComponent the ticket component */ public GetActiveUsersWebscript(final TicketComponent ticketComponent) { super(); this.ticketComponent = ticketComponent; } /* (non-Javadoc) * @see org.springframework.extensions.webscripts.DeclarativeWebScript#executeImpl(org.springframework.extensions.webscripts.WebScriptRequest, org.springframework.extensions.webscripts.Status, org.springframework.extensions.webscripts.Cache) */ @Override public Map<String, Object> executeImpl(final WebScriptRequest req, final Status status, final Cache cache) { if(LOGGER.isDebugEnabled()) { LOGGER.debug("Extracting active users.."); } final Map<String, Object> model = new ConcurrentHashMap<String, Object>(3); try { //get nonExpiredOnly users with tickets final Set<String> activeUsers = ticketComponent.getUsersWithTickets(true); final ObjectMapper objMapper = new ObjectMapper(); objMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); if (activeUsers != null && !activeUsers.isEmpty()) { final JSONObject activeUsersJson = new JSONObject(); //This may be lower than the ticket count, since a user can have more than one // ticket/session activeUsersJson.put(ACTIVE_USERS, objMapper.writeValueAsString(activeUsers)); activeUsersJson.put(ACTIVE_USERS_COUNT, activeUsers.size()); //This may be higher than the user count, since a user can have more than one // ticket/session //get nonExpiredOnly ticket count activeUsersJson.put(ACTIVE_TICKETS_COUNT, ticketComponent.countTickets(true)); activeUsersJson.put(COMMENT_DATA, "Active user count may be lower than the ticket count, since a user can have more than one ticket/session. Ticket count may be higher than the active user count, since a user can have more than one ticket/session."); model.put(RESPONSE, activeUsersJson); } } catch (JsonProcessingException | JSONException excp) { LOGGER.error("Exception occurred while preparing json for active users ", excp); throw new WebScriptException( Status.STATUS_INTERNAL_SERVER_ERROR, excp.getMessage(), excp); } catch (AlfrescoRuntimeException alfErr) { LOGGER.error("Unexpected error occurred while getting active users ", alfErr); throw new WebScriptException( Status.STATUS_INTERNAL_SERVER_ERROR, alfErr.getMessage(), alfErr); } if(LOGGER.isDebugEnabled()) { LOGGER.debug("Extracted active users."); } return model; } }
- Add the bean definition for the java webscript (GetActiveUsersWebscript) in spring context file.
<bean id="webscript.com.github.abhinavmishra14.audit.getActiveUsers.get" class="com.github.abhinavmishra14.audit.webscript.GetActiveUsersWebscript" parent="webscript"> <constructor-arg ref="ticketComponent" /> </bean>
- Build and start the alfresco instance.
- Access the service using URL: http://127.0.0.1:8080/alfresco/service/audit/getActiveUsers (You will be prompted for credentials, note that the user should be admin who is accessing this service)
- It will return following type of response (example):
{ "activeTicketsCount": 3, "activeUsersCount": 3, "_comment": "Active user count may be lower than the ticket count, since a user can have more than one ticket/session. Ticket count may be higher than the active user count, since a user can have more than one ticket/session.", "activeUsers": "[\"test2\",\"admin\",\"guest\"]" }
Refer this github project in case you need to refer to the code: https://github.com/abhinavmishra14/active-users-report
Alternatively, you can apply "active-users-report-1.0-SNAPSHOT.amp" to your alfresco installation as an add-on.
you java, at line number : 70, DeserializationFeature, giving me error, as Cannot resolve symbol, did you have anything that need to be imported here?
ReplyDeletejava is not able to compile,
Which version of Alfresco you are using ? This code is tested with 5.0 and works with 5.x and onwards.
DeleteI think you are missing JSON related dependencies (probably jackson-databind) which are mandatory for the code to work.
Please make sure that you have following dependencies:
· jackson-databind
· jackson-core
· jackson-annotations
You can download these dependencies from here: http://mvnrepository.com/search?q=com.fasterxml.jackson.core
Additionally your build path should have slf4j and log4j dependencies.
ERROR [extensions.webscripts.AbstractRuntime] [http-bio-8080-exec-12] Exception from executeScript: 00050008 Wrapped Exception (with status template): 00050011 Error during processing of the template 'The following has evaluated to null or missing:
ReplyDelete==> jsonUtils.encodeJSONString(x) [in template "com/eisenvault/getActiveUsers.get.json.ftl" at line 1, column 15]
Tip: If the failing expression is known to be legally null/missing, either specify a default value with myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing. (These only cover the last step of the expression; to cover the whole expression, use parenthessis: (myOptionVar.foo)!myDefault, (myOptionVar.foo)??
The failing instruction:
==> ${response} auto-escaped [in template "com/eisenvault/getActiveUsers.get.json.ftl" at line 1, column 46]'. Please contact your system administrator.
org.springframework.extensions.webscripts.WebScriptException: 00050008 Wrapped Exception (with status template): 00050011 Error during processing of the template 'The following has evaluated to null or missing:
==> jsonUtils.encodeJSONString(x) [in template "com/eisenvault/getActiveUsers.get.json.ftl" at line 1, column 15]
Tip: If the failing expression is known to be legally null/missing, either specify a default value with myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing. (These only cover the last step of the expression; to cover the whole expression, use parenthessis: (myOptionVar.foo)!myDefault, (myOptionVar.foo)??
getting this error in log
See the error, it says there is null in the response. Like i mentioned there is some glicth which is not returning the required to the template. Try and debug the issue. Must be a code glitch.
DeleteI have exported the code to git so you can also refer: https://github.com/abhinavmishra14/active-users-report
Alternatively, just download active-users-report-1.0-SNAPSHOT.amp (https://github.com/abhinavmishra14/active-users-report/releases/download/active-users-report-v1.0/active-users-report-1.0-SNAPSHOT.amp) and install on alfresco.war file and consume the response from http://127.0.0.1:8080/alfresco/service/audit/getActiveUsers