Title_Documentation

Documentation

Contact Us

Knowledge Base Display

Define Custom Permission System

As was mentioned earlier HyperIoT allows the ability to support multiple permission systems simultaneously.

It now remains to figure out how to develop a Custom permission system. First, below is the HyperIoTPermissionManager interface that needs to be implemented.

HyperIoTPermissionManager ``` package it.acsoftware.hyperiot.base.api;

import it.acsoftware.hyperiot.base.api.entity.HyperIoTProtectedEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory;

public interface HyperIoTPermissionManager { Logger log = LoggerFactory.getLogger(HyperIoTPermissionManager.class.getName());

boolean userHasRoles(String var1, String[] var2);

boolean checkPermission(String var1, HyperIoTResource var2, HyperIoTAction var3);

boolean checkPermission(String var1, Class<? extends HyperIoTResource> var2, HyperIoTAction var3);

boolean checkPermission(String var1, String var2, HyperIoTAction var3);

boolean checkPermissionAndOwnership(String var1, String var2, HyperIoTAction var3, HyperIoTResource... var4);

boolean checkPermissionAndOwnership(String var1, HyperIoTResource var2, HyperIoTAction var3, HyperIoTResource... var4);

boolean checkUserOwnsResource(HyperIoTUser var1, Object var2);

static boolean isProtectedEntity(Object entity) {
    log.debug("invoking Permission Manager getProtectedEntity " + entity.getClass().getSimpleName());
    if (entity instanceof HyperIoTProtectedEntity) {
        return true;
    } else {
        return entity instanceof HyperIoTProtectedResource;
    }
}

static boolean isProtectedEntity(String resourceName) {
    log.debug("invoking Permission getProtectedEntity " + resourceName);

    try {
        boolean isAssignable = HyperIoTProtectedEntity.class.isAssignableFrom(Class.forName(resourceName));
        isAssignable = isAssignable || HyperIoTProtectedResource.class.isAssignableFrom(Class.forName(resourceName));
        return isAssignable;
    } catch (ClassNotFoundException var2) {
        log.warn(var2.getMessage());
        return true;
    }
}

} ```

The interface is very simple and generic. In fact it uses only abstract platform concepts such as HyperIoTResource or HyperIoTUser (user abstraction that is different from the HUser entity).

In order to have a valid implementation of a permission system all we need to do is to define a component that implements such an interface and has an identifying string that can discriminate it from other existing permission systems.

Following is , as an example, the implementation of the default permission system that itself an extension component (used as a default) of the HyperIoT permission system.

package it.acsoftware.hyperiot.permission.service;

import it.acsoftware.hyperiot.base.action.util.HyperIoTActionsUtil;
import it.acsoftware.hyperiot.base.api.*;
import it.acsoftware.hyperiot.base.api.entity.*;
import it.acsoftware.hyperiot.base.util.HyperIoTConstants;
import it.acsoftware.hyperiot.base.util.HyperIoTUtil;
import it.acsoftware.hyperiot.huser.actions.HyperIoTHUserAction;
import it.acsoftware.hyperiot.huser.api.HUserSystemApi;
import it.acsoftware.hyperiot.huser.model.HUser;
import it.acsoftware.hyperiot.permission.api.PermissionSystemApi;
import it.acsoftware.hyperiot.permission.model.Permission;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import javax.persistence.NoResultException;
import java.util.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Aristide Cittadino Implementation class of HyperIoTPermissionManager
 * interface. It is used to implement all methods able to check if a
 * user has permissions through every actions of the HyperIoTAction.
 */
@Component(service = HyperIoTPermissionManager.class, property = {
    HyperIoTConstants.OSGI_PERMISSION_MANAGER_IMPLEMENTATION
        + "=default"}, immediate = true, servicefactory = false)
public class PermissionManagerDefault implements HyperIoTPermissionManager {
    private Logger log = LoggerFactory.getLogger(PermissionManagerDefault.class.getName());
    /**
     * Injecting the PermissionSystemService to use methods in PermissionSystemApi
     * interface
     */
    private PermissionSystemApi systemService;
    /**
     * Injecting the HUserSystemService to use methods in HUserSystemApi interface
     */
    private HUserSystemApi huserSystemService;

    /**
     * Injecting the SharedEntitySystemService to use methods in SharedEntitySystemApi interface
     */
    private HyperIoTSharingEntityService sharedEntityService;


    /**
     * @param username
     * @param rolesNames
     * @return
     */
    @Override
    public boolean userHasRoles(String username, String[] rolesNames) {
        if (username == null || username.length() == 0)
            return false;
        HUser user = huserSystemService.findUserByUsername(username);
        if (user == null)
            return false;
        Collection<String> rolesNamesCollection = Arrays.asList(rolesNames.clone());
        return user.getRoles().stream().anyMatch(r -> rolesNamesCollection.contains(r));
    }

    /**
     * Checks if an existing user has permissions for action of HyperIoTAction.
     * Moreover every user, if protected, is set as a base entity of the HyperIoT
     * platform.
     *
     * @param username parameter that indicates the username of user
     * @param entity   parameter that indicates the resource name of user
     * @param action   interaction of the user with HyperIoT platform
     */
    @Override
    public boolean checkPermission(String username, HyperIoTResource entity,
                                   HyperIoTAction action) {
        if (!HyperIoTPermissionManager.isProtectedEntity(entity.getResourceName()))
            return true;
        log.debug(
            "invoking checkPermission User {} Entity Resource Name: {}", new Object[]{username, entity.getResourceName(), action.getResourceName(), action.getActionId()});
        if (!HyperIoTPermissionManager.isProtectedEntity(entity))
            return true;

        if (username == null || entity == null || action == null)
            return false;

        HyperIoTUser user = this.huserSystemService.findUserByUsername(username);

        // every protected entity is a base entity
        HyperIoTProtectedEntity entityResource = (HyperIoTProtectedEntity) entity;

        return hasPermission(user, entityResource, action);
    }

    /**
     * Checks if an existing user has permissions for action of HyperIoTAction.
     *
     * @param username     parameter that indicates the username of user
     * @param resourceName parameter that indicates the resource name of action
     * @param action       interaction of the user with HyperIoT platform
     */
    @Override
    public boolean checkPermission(String username, String resourceName, HyperIoTAction action) {
        if (!HyperIoTPermissionManager.isProtectedEntity(resourceName))
            return true;
        log.debug(
            "invoking checkPermission User {} Entity Resource Name: {} Action Name: {}  actionId: {}", new Object[]{username, resourceName, action.getResourceName(), action.getActionId()});
        if (username == null || resourceName == null || action == null)
            return false;

        if (!HyperIoTPermissionManager.isProtectedEntity(resourceName))
            return true;

        return hasPermission(username, resourceName, action);
    }

    /**
     * Checks if an existing user has permissions for action of HyperIoTAction.
     *
     * @param username parameter that indicates the username of user
     * @param resource parameter that indicates the resource name of action
     * @param action   interaction of the user with HyperIoT platform
     */
    @Override
    public boolean checkPermission(String username, Class<? extends HyperIoTResource> resource,
                                   HyperIoTAction action) {
        if (!HyperIoTPermissionManager.isProtectedEntity(resource.getName()))
            return true;
        log.debug(
            "invoking checkPermission User {} Entity Resource Name: {} Action Name: {}  actionId: {}", new Object[]{username, resource.getName(), action.getResourceName(), action.getActionId()});
        if (username == null || resource == null || action == null)
            return false;

        return hasPermission(username, resource.getName(), action);
    }

    /**
     * @param username     parameter that indicates the username of entity
     * @param resourceName parameter that indicates the resource name of action
     * @param action       interaction of the user with HyperIoT platform
     * @param entities     List of entities User must own in order to perform the action
     * @return
     */
    public boolean checkPermissionAndOwnership(String username, String resourceName, HyperIoTAction action, HyperIoTResource... entities) {
        boolean hasPermission = false;
        if (!HyperIoTPermissionManager.isProtectedEntity(resourceName))
            hasPermission = true;
        else
            hasPermission = checkPermission(username, resourceName, action);

        if (hasPermission && entities != null) {
            HyperIoTUser user = this.huserSystemService.findUserByUsername(username);
            for (int i = 0; i < entities.length && hasPermission; i++) {
                hasPermission = hasPermission && user != null && entities[i] != null && checkUserOwnsResource(user, entities[i]);
            }
        }
        return hasPermission;
    }

    /**
     * @param username parameter that indicates the username of entity
     * @param resource parameter that indicates the resource on which the action should be performed
     * @param action   interaction of the user with HyperIoT platform
     * @param entities List of other entities User must own in order to perform the action
     * @return
     */
    public boolean checkPermissionAndOwnership(String username, HyperIoTResource resource, HyperIoTAction action, HyperIoTResource... entities) {
        boolean hasPermission = false;
        if (!HyperIoTPermissionManager.isProtectedEntity(resource.getResourceName()))
            hasPermission = true;
        else
            hasPermission = checkPermission(username, resource.getResourceName(), action);

        if (hasPermission && entities != null) {
            HyperIoTUser user = this.huserSystemService.findUserByUsername(username);
            for (int i = 0; i < entities.length && hasPermission; i++) {
                hasPermission = hasPermission && user != null && entities[i] != null && checkUserOwnsResource(user, entities[i]);
            }
        }
        return hasPermission;
    }

    /**
     * @return The current PermissionSystemService
     */
    public PermissionSystemApi getSystemService() {
        log.debug( "invoking getSystemService, returning: {}", this.systemService);
        return systemService;
    }

    /**
     * @param systemService Injecting via OSGi DS current PermissionSystemService
     */
    @Reference
    protected void setSystemService(PermissionSystemApi systemService) {
        log.debug( "invoking setSystemService, setting: {}", systemService);
        this.systemService = systemService;
    }

    /**
     * @return The current HUserSystemService
     */
    public HUserSystemApi getHuserSystemService() {
        log.debug( "invoking getSystemService, returning: {}", this.huserSystemService);
        return huserSystemService;
    }

    /**
     * @param huserSystemService Injecting via OSGi DS current HUserSystemService
     */
    @Reference
    public void setHuserSystemService(HUserSystemApi huserSystemService) {
        log.debug( "invoking setHUserSystemService, setting: {}", huserSystemService);
        this.huserSystemService = huserSystemService;
    }

    /**
     * @param sharedEntitySystemService Injecting via OSGi DS current SharedEntitySystemService
     */
    @Reference
    public void setSharedEntitySystemService(HyperIoTSharingEntityService sharedEntitySystemService) {
        log.debug( "invoking setSharedEntitySystemService, setting: {}", sharedEntitySystemService);
        this.sharedEntityService = sharedEntitySystemService;
    }

    /**
     * Find an existing user by username. Returns actions permission by user role.
     *
     * @param username     parameter required to find a user by his username
     * @param resourceName parameter that indicates the resource name
     * @param action       interaction of the user with HyperIoT platform
     * @return Actions permission by user
     */
    private boolean hasPermission(String username, String resourceName, HyperIoTAction action) {
        HyperIoTUser user = this.huserSystemService.findUserByUsername(username);
        if (user == null) {
            return false;
        }

        if (user.isAdmin())
            return true;

        if (user.getRoles() == null)
            return false;

        Iterator<? extends HyperIoTRole> it = user.getRoles().iterator();

        while (it.hasNext()) {
            HyperIoTRole r = it.next();
            Permission permission = systemService.findByRoleAndResourceName(r, resourceName);
            if (permission != null
                && hasPermission(permission.getActionIds(), action.getActionId()))
                return true;
        }
        return false;
    }

    /**
     * Find an existing user by username. Returns actions permission by user role.
     *
     * @param user   parameter required to find a user by his username
     * @param action interaction of the user with HyperIoT platform
     * @return Actions permission by user
     */
    private boolean hasPermission(HyperIoTUser user, HyperIoTProtectedEntity entity,
                                  HyperIoTAction action) {
        if (user == null) {
            return false;
        }

        if (user.isAdmin())
            return true;

        if (user.getRoles() == null)
            return false;

        Iterator<? extends HyperIoTRole> it = user.getRoles().iterator();
        while (it.hasNext()) {
            HyperIoTRole r = it.next();
            Permission permission = null;
            try {
                permission = systemService.findByRoleAndResourceName(r, entity.getResourceName());
            } catch (NoResultException e) {
                log.warn( "No permission found for: {}  on resource {}"
                    , new Object[]{r.getName(), entity.getResourceName()});
            }

            Permission permissionSpecific = null;
            try {
                permissionSpecific = systemService.findByRoleAndResourceNameAndResourceId(r,
                    entity.getResourceName(), entity.getId());
            } catch (NoResultException e) {
                log.warn( "No specific permission found for: {}  on resource {}"
                    , new Object[]{r.getName(), entity.getResourceName()});
            }
            Permission permissionImpersonation = null;
            try {
                permissionImpersonation = systemService.findByRoleAndResourceName(r,
                    HUser.class.getName());
            } catch (NoResultException e) {
                log.warn( "No impersonification permission found for: {}  on resource {}"
                    , new Object[]{r.getName(), entity.getResourceName()});
            }
            // it initialize the value with the general value based on resource name
            boolean hasGeneralPermission = permission != null
                && hasPermission(permission.getActionIds(), action.getActionId());
            boolean hasEntityPermission = (permissionSpecific != null
                && hasPermission(permissionSpecific.getActionIds(), action.getActionId()));

            HyperIoTAction impersonateAction = HyperIoTActionsUtil
                .getHyperIoTAction(HUser.class.getName(), HyperIoTHUserAction.IMPERSONATE);
            boolean userOwnsResource = checkUserOwnsResource(user, entity);
            boolean userSharesResource = checkUserSharesResource(user, entity);
            boolean hasImpersonationPermission = permissionImpersonation != null && hasPermission(
                permissionImpersonation.getActionIds(), impersonateAction.getActionId());
            // The value is true only if the entity permission exists and contains the
            // actionId, or if
            // the entity permission doesn't exists then the rule follow the
            // generalPermission
            // AND if the resource is an owned resource is accessed by the right user or the
            // accessing user has the impersonation permission
            if ((hasEntityPermission || (permissionSpecific == null && hasGeneralPermission))
                && (userOwnsResource || ((userSharesResource && permissionSpecific != null && hasEntityPermission)) || hasImpersonationPermission))
                return true;
        }
        return false;
    }

    /**
     * Performs a bitwise operation between the permissionActionIds and the
     * actionId. It manipulate the bits with & operator used to compare bits of each
     * operand.
     *
     * @param permissionActionIds parameter that indicates the Permission actionIds
     * @param actionId            parameter that indicates the id of HyperIoTAction
     */
    private boolean hasPermission(int permissionActionIds, int actionId) {
        boolean hasPermission = (permissionActionIds & actionId) == actionId;
        log.debug(
            "invoking hasPermission permissionActionIds & actionId == actionId {}",
            new Object[]{permissionActionIds, actionId, hasPermission});
        return hasPermission;
    }

    /**
     * @param user     the current logged user
     * @param resource the current resource
     * @return true if the resource is owned by the current logged user or the
     * resource is not a owned resource, false otherwise.
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public boolean checkUserOwnsResource(HyperIoTUser user, Object resource) {
        if (user.isAdmin())
            return true;

        HyperIoTBaseEntity entity = (HyperIoTBaseEntity) resource;
        HyperIoTUser resourceOwner = null;
        // looks up for a persisted entity in the hierarchy chain
        while (true) {
            // double check if the passed entity is consistent (must belong to `user`)
            if (entity instanceof HyperIoTOwnedResource) {
                resourceOwner = ((HyperIoTOwnedResource) entity).getUserOwner();
                if (resourceOwner != null && resourceOwner.getId() != 0
                    && user.getId() != resourceOwner.getId()) {
                    return false;
                } else
                    break;
            } else if (entity instanceof HyperIoTOwnedChildResource) {
                HyperIoTOwnedChildResource child = (HyperIoTOwnedChildResource) entity;
                if (child.getParent() != null)
                    entity = child.getParent();
                else
                    break;
            } else
                break;
        }
        if (entity.getId() != 0) {
            // load the persisted entity
            BundleContext context = HyperIoTUtil.getBundleContext(this);
            ServiceReference serviceReference = context
                .getServiceReference(entity.getSystemApiClassName());
            HyperIoTBaseEntitySystemApi service = (HyperIoTBaseEntitySystemApi) context
                .getService(serviceReference);
            HyperIoTBaseEntity persistedEntity = service.find(entity.getId(), null);
            // verify the owner
            if (persistedEntity instanceof HyperIoTOwnedResource) {
                resourceOwner = ((HyperIoTOwnedResource) persistedEntity).getUserOwner();
            } else if (persistedEntity instanceof HyperIoTOwnedChildResource) {
                HyperIoTOwnedChildResource persistedChildEntity = (HyperIoTOwnedChildResource) persistedEntity;
                if (persistedChildEntity != null && persistedChildEntity.getParent() != null) {
                    // retry with the parent resource
                    return (persistedChildEntity.getParent() == null || checkUserOwnsResource(user, persistedChildEntity.getParent()))
                        && checkUserOwnsResource(user, persistedChildEntity.getParent());
                }
            } else {
                // resource is not owned so check can pass
                return (persistedEntity != null);
            }
        } else {
            return true;
        }
        return (user != null && resourceOwner != null && user.getId() == resourceOwner.getId());
    }

    /**
     * @param user     the current logged user
     * @param resource the current resource
     * @return true if the resource is shared to the current logged user or the
     * resource is not a shared resource, false otherwise.
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private boolean checkUserSharesResource(HyperIoTUser user, Object resource) {
        if (user.isAdmin())
            return true;
        HyperIoTBaseEntity entity = (HyperIoTBaseEntity) resource;
        List<HyperIoTUser> sharingUsers = new ArrayList<>();
        // looks up for a persisted entity in the hierarchy chain
        boolean loop = true;
        while (loop) {
            // double check if the passed entity is consistent (must be shared to `user`)
            if (entity instanceof HyperIoTSharedEntity) {
                sharingUsers = sharedEntityService.getSharingUsers(entity.getResourceName(), entity.getId(), null);
                if (sharingUsers.stream().noneMatch(u -> u.getId() == user.getId())) {
                    return false;
                } else
                    loop = false;
            } else if (entity instanceof HyperIoTOwnedChildResource) {
                HyperIoTOwnedChildResource child = (HyperIoTOwnedChildResource) entity;
                if (child.getParent() != null)
                    entity = child.getParent();
                else
                    loop = false;
                ;
            } else
                loop = false;
            ;
        }
        if (entity.getId() != 0) {
            // load the persisted entity
            BundleContext context = HyperIoTUtil.getBundleContext(this);
            ServiceReference serviceReference = context
                .getServiceReference(entity.getSystemApiClassName());
            HyperIoTBaseEntitySystemApi service = (HyperIoTBaseEntitySystemApi) context
                .getService(serviceReference);
            HyperIoTBaseEntity persistedEntity = service.find(entity.getId(), null);
            // verify the owner
            if (persistedEntity instanceof HyperIoTSharedEntity) {
                sharingUsers = sharedEntityService.getSharingUsers(entity.getResourceName(), entity.getId(), null);
            } else if (persistedEntity instanceof HyperIoTOwnedChildResource) {
                HyperIoTOwnedChildResource persistedChildEntity = (HyperIoTOwnedChildResource) persistedEntity;
                if (persistedChildEntity != null && persistedChildEntity.getParent() != null) {
                    // retry with the parent resource
                    return (persistedChildEntity.getParent() == null || checkUserOwnsResource(user, persistedChildEntity.getParent()))
                        && checkUserOwnsResource(user, persistedChildEntity.getParent());
                } else {
                    // resource is not shared so check can pass
                    return (persistedEntity != null);
                }
            }
        } else {
            return false;
        }
        return (user != null && sharingUsers.stream().anyMatch(u -> u.getId() == user.getId()));
    }

}

The key part of the definition is found from lines 30 to 33: Basically, you register the OSGi component and as an identifying string for the permission manager you give the word “default.”

@Component(service = HyperIoTPermissionManager.class, property = {
    HyperIoTConstants.OSGI_PERMISSION_MANAGER_IMPLEMENTATION
        + "=default"}, immediate = true, servicefactory = false)
public class PermissionManagerDefault implements HyperIoTPermissionManager
Securing Services Previous