Creating your own security access manager

The security access manager is used by the OpenKM to check the user security access level of the nodes.

Conditions:

  • The new Principal Adapter class must implement the "DbAccessManager" interface.
  • The new Principal Adapter class must be declared under the package "com.openkm.plugin.access".
  • The new Principal Adapter class must be annotated with "@PluginImplementation".
  • The new Principal Adapter class must extend of "BasePlugin".

Security access manager interface:

package com.openkm.plugin.access;

import com.openkm.core.AccessDeniedException;
import com.openkm.core.DatabaseException;
import com.openkm.core.PathNotFoundException;
import com.openkm.db.bean.NodeBase;
import com.openkm.principal.PrincipalAdapterException;
import net.xeoh.plugins.base.Plugin;

/**
 * Check user permissions on documents and folders.
 *
 * @author pavila
 */
public interface DbAccessManager extends Plugin {

	void checkPermission(NodeBase node, int permissions) throws AccessDeniedException, PathNotFoundException, DatabaseException;

	boolean isGranted(NodeBase node, int permissions) throws DatabaseException, PathNotFoundException;

	boolean isGranted(NodeBase node, String user, int permissions) throws PrincipalAdapterException, DatabaseException, PathNotFoundException;

	boolean isGranted(NodeBase node, long nodeClass, int permissions) throws DatabaseException;
}

The new class must be loaded into the package com.openkm.plugin.access because application plugins system will try to load from there.

Do not miss the tag @PluginImplementation otherwise, the application plugin system will not be able to retrieve the new class.

More information at Register a new plugin.

To enable the new Security access manager go to Administration > Configuration parameters > find the parameter named security.access.manager and modify the value with com.openkm.plugin.access.OwnSecurityManager

To take effect this change we need to restart the application.

Methods description

In the next table the values of permissions are ( more information in the class Permission.java ):

0 NONE
1 READ
2 WRITE
4 DELETE
8 SECURITY
16 MOVE
1024 DOWNLOAD
2048 START WORKFLOW
4096 COMPACT HISTORY
8192 METADATA

 

MethodTypeDescription
checkPermission(NodeBase node, int permissions) void

Description

isGranted(NodeBase node, int permissions) Boolean

Return true when the user in the session has the grant.

isGranted(NodeBase node, String user, int permissions)   Boolean Return true when the user has the grant.

isGranted(NodeBase node, long nodeClass, int permissions)

Boolean

 

Return true when the user in the session has the grant.

This method only has sense when file plan is enabled.

Example of Security access manager implementation

package com.openkm.plugin.access;

import com.openkm.core.AccessDeniedException;
import com.openkm.core.Config;
import com.openkm.core.DatabaseException;
import com.openkm.core.PathNotFoundException;
import com.openkm.db.bean.NodeBase;
import com.openkm.db.bean.NodeClass;
import com.openkm.db.service.NodeBaseSrv;
import com.openkm.db.service.NodeClassSrv;
import com.openkm.module.common.CommonAuthModule;
import com.openkm.plugin.BasePlugin;
import com.openkm.plugin.fileplan.security.FilePlanAccessManager;
import com.openkm.principal.PrincipalAdapterException;
import com.openkm.principal.PrincipalUtils;
import com.openkm.util.StackTraceUtils;
import net.xeoh.plugins.base.annotations.PluginImplementation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Check user permissions on documents and folders.
 *
 * @see com.openkm.plugin.access.DbRecursiveAccessManager
 */
@PluginImplementation
public class DbSimpleAccessManager extends BasePlugin implements DbAccessManager {
	private static final Logger log = LoggerFactory.getLogger(DbSimpleAccessManager.class);

	@Autowired
	private CommonAuthModule commonAuthModule;

	@Autowired
	private NodeClassSrv nodeClassSrv;

	@Autowired
	private NodeBaseSrv nodeBaseSrv;

	/**
	 * Check for permissions.
	 */
	public void checkPermission(NodeBase node, int permissions) throws AccessDeniedException, PathNotFoundException, DatabaseException {
		if (!isGranted(node, permissions)) {
			String nodePath = nodeBaseSrv.getPathFromUuid(node.getUuid());
			throw new AccessDeniedException(node.getUuid() + " : " + nodePath);
		}
	}

	/**
	 * Check for permissions.
	 */
	@Override
	public boolean isGranted(NodeBase node, int permissions) throws DatabaseException {
		String userId = PrincipalUtils.getUser();
		Set<String> roles = PrincipalUtils.getRoles();
		return isGranted(node, userId, roles, permissions);
	}

	/**
	 * Check for permissions.
	 */
	@Override
	public boolean isGranted(NodeBase node, String user, int permissions) throws PrincipalAdapterException, DatabaseException {
		List<String> roles = commonAuthModule.getRolesByUser(user);
		return isGranted(node, user, new HashSet<>(roles), permissions);
	}

	@Override
	public boolean isGranted(NodeBase node, long nodeClass, int permissions) throws DatabaseException {
		NodeClass nc = nodeClassSrv.findByPk(nodeClass);
		FilePlanAccessManager accessManager = nodeClassSrv.findAccessManagerByClassName(nc.getAccessManagerClassName());
		return accessManager.isGranted(node, nodeClass, PrincipalUtils.getUser(), PrincipalUtils.getRoles(), permissions);
	}

	/**
	 * Check for permissions.
	 */
	private boolean isGranted(NodeBase node, String user, Set<String> roles, int permissions) {
		log.debug("isGranted({}, {})", node.getUuid(), permissions);
		boolean access;

		if (user != null) {
			if (Config.SYSTEM_USER.equals(user) || Config.ADMIN_USER.equals(user)) {
				// An okmAdmin user has total access
				access = true;
			} else {
				if (roles.contains(Config.DEFAULT_ADMIN_ROLE)) {
					// An user with AdminRole has total access
					access = true;
				} else {
					access = checkProperties(node.getUserPermissions(), node.getRolePermissions(), user, roles, permissions);
				}
			}
		} else {
			access = true;

			log.info("*****************************************************");
			StackTraceUtils.logTrace(log);
			log.info("*****************************************************");
		}

		log.debug("isGranted: {}", access);
		return access;
	}

	/**
	 * Check access properties
	 */
	private boolean checkProperties(Map<String, Integer> usersPerms, Map<String, Integer> rolesPerms, String user,
			Set<String> roles, int perms) {
		log.debug("checkProperties({}, {}, {}, {})", usersPerms, rolesPerms, roles, perms);
		boolean access = false;

		// Fist try with user permissions
		Integer userPerms = usersPerms.get(user);

		if (userPerms != null && (perms & userPerms) != 0) {
			log.debug("checkProperties: {}", true);
			return true;
		}

		// If there is no user specific access, try with roles permissions
		for (String role : roles) {
			Integer rolePerms = rolesPerms.get(role);

			if (rolePerms != null && (perms & rolePerms) != 0) {
				log.debug("checkProperties: {}", true);
				return true;
			}
		}

		log.debug("checkProperties: {}", access);
		return access;
	}
}