How to use the RoleRegistry service in its own bundle? (I have implemented the RoleRegistry service)

Hi everyone,

I am currently implementing the role-based access control model for openhab users. For this, I am modifying the openhab-core project and need to store and manage roles in a registry in the same way as for users and items.

For the user the UserRegistryImpl.class does this functionality and for the items the ItemRegistryImpl.class does this functionality. Therefore, I implemented the RoleRegistryImpl.class.

Here you can see my class RoleRegistryImpl.class put at the location /openhab-core/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/roles/RoleRegistryImpl.java:

package org.openhab.core.internal.roles;

import java.util.HashSet;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.*;
import org.openhab.core.common.registry.AbstractRegistry;
import org.openhab.core.items.ItemRegistry;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.*;

/**
 * @author Nicolas Gennart
 */
@NonNullByDefault
@Component(service = RoleRegistry.class, immediate = true)
public class RoleRegistryImpl extends AbstractRegistry<Role, String, RoleProvider> implements RoleRegistry {

    private final ItemRegistry itemRegistry;

    /**
     * Constructor.
     *
     */
    @Activate
    public RoleRegistryImpl(BundleContext bundleContext, @Reference ItemRegistry itemRegistry) {
        super(RoleProvider.class);
        super.activate(bundleContext);
        this.itemRegistry = itemRegistry;
    }

    @Override
    @Deactivate
    protected void deactivate() {
        super.deactivate();
    }

    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
    protected void setManagedProvider(ManagedRoleProvider managedProvider) {
        super.setManagedProvider(managedProvider);
        super.addProvider(managedProvider);
    }

    protected void unsetManagedProvider(ManagedRoleProvider managedProvider) {
        super.unsetManagedProvider(managedProvider);
        super.removeProvider(managedProvider);
    }

    @Override
    public void updateRole(String oldRole, String newRole) {
        ManagedRole managedrole = new ManagedRole(newRole);
        // We check if the oldRole exist.
        if (get(oldRole) != null) {
            if (newRole.equals("administrator")) {
                managedrole.setItemNames(itemRegistry.getAllItemNames());
            }
            remove(oldRole);
            add(managedrole);
        } else {
            throw new IllegalArgumentException(
                    "The role " + oldRole + " does not exist in the roleRegistry so we can't change it.");
        }
    }

    @Override
    public void addRole(String role) {
        ManagedRole managedrole = new ManagedRole(role);
        // We check if the role does not exist.
        if (get(role) == null) {
            if (role.equals("administrator")) {
                managedrole.setItemNames(itemRegistry.getAllItemNames());
            }
            add(managedrole);
        } else {
            throw new IllegalArgumentException(
                    "The role " + role + " already exist in the roleRegistry so we can not add it.");
        }
    }

    @Override
    public void removeRole(String role) {

        // We check if the role exist.
        if (get(role) != null) {
            remove(role);
        } else {
            throw new IllegalArgumentException(
                    "The role " + role + " does not exist in the roleRegistry so we can not remove it.");
        }
    }

    @Override
    public void addItemsToRole(String role, HashSet<String> itemNames) {
        ManagedRole managedRole = (ManagedRole) get(role);
        // We check if the role in the registry exist.
        if (managedRole != null) {
            HashSet<String> roleItemNames = (HashSet<String>) managedRole.getItemNames();
            // We check if the set changed
            if (roleItemNames.addAll(itemNames)) {
                managedRole.setItemNames(roleItemNames);
                update(managedRole);
            }
        } else {
            throw new IllegalArgumentException(
                    "The role " + role + " does not exist in the roleRegistry so we can not add items to it.");
        }
    }

    @Override
    public void removeItemsToRole(String role, HashSet<String> itemNames) {
        ManagedRole managedRole = (ManagedRole) get(role);
        // We check if the role in the registry exist.
        if (managedRole != null) {
            HashSet<String> roleItemNames = (HashSet<String>) managedRole.getItemNames();
            // We check if the set changed
            if (roleItemNames.removeAll(itemNames)) {
                managedRole.setItemNames(roleItemNames);
                update(managedRole);
            }
        } else {
            throw new IllegalArgumentException(
                    "The role " + role + " does not exist in the roleRegistry so we can not remove items to it.");
        }
    }
}

I implemented the ManageRole.class which implements the Role interface at the location openhab-core/bundles/org.openhab.core/src/main/java/org/openhab/core/auth/ManagedRole.java as shown below:

package org.openhab.core.auth;

import java.util.HashSet;
import java.util.Set;

/**
 * @author Nicolas Gennart
 */
public class ManagedRole implements Role {

    private final String role;

    private Set<String> itemNames = new HashSet<>();

    public ManagedRole(String role) {
        this.role = role;
    }

    @Override
    public String getRole() {
        return role;
    }

    @Override
    public String getUID() {
        return role;
    }

    public Set<String> getItemNames() {
        return itemNames;
    }

    public void setItemNames(Set<String> itemNames) {
        this.itemNames = itemNames;
    }
}

The interface RoleProvider like that:

package org.openhab.core.auth;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.common.registry.Provider;

/**
 * @author Nicolas Gennart
 */
@NonNullByDefault
public interface RoleProvider extends Provider<Role> {
}

And the last class I need to implement is the ManagedRoleProvider.class at the location openhab-core/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/roles/ManagedRoleProvider.java to provide the Role objects and store them in a file as below :

package org.openhab.core.internal.roles;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.ManagedUser;
import org.openhab.core.auth.Role;
import org.openhab.core.common.registry.DefaultAbstractManagedProvider;
import org.openhab.core.common.registry.ManagedProvider;
import org.openhab.core.storage.StorageService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
 * A {@link ManagedProvider} for {@link ManagedUser} entities
 *
 * @author Yannick Schaus - initial contribution
 */
@NonNullByDefault
@Component(service = ManagedRoleProvider.class, immediate = true)
public class ManagedRoleProvider extends DefaultAbstractManagedProvider<Role, String> {

    @Activate
    public ManagedRoleProvider(final @Reference StorageService storageService) {
        super(storageService);
    }

    @Override
    protected String getStorageName() {
        return "roles";
    }

    @Override
    protected String keyToString(String key) {
        return key;
    }
}

So, the RoleRegistry works fine when I call it in another bundle but when I call the RoleRegistry.class in the same bundle (in the org.openhab.core bundle), it does not work and the org.openhab.core bundle remains in the Waiting state.

This is strange because I implemented the RoleRegistry exactly the same way as the UserRegistry and the UserRegistry can be called in another class in the same bundle. I also can’t test the RoleRegistryImpl.class in the folder test of the org.openhab.core bundle because I can’t call the RoleRegistry.class in the same bundle.

Does anyone know what I need to do for the RoleRegistry and how it is done for the UserRegistry or for the ItemRegistry so that the RoleRegistry service can be used in its own bundle?

Thank you very much,

Nicolas Gennart.

PS: The code guidelines are not yet fully respected (like the import *) but I will do it later.

1 Like

I found the error, it was because they had loops in my dependencies of my services.

Not sure where your work will lead, but please consider definition of permissions as part of your task so these managed roles could be arranged to something more than “user” and “admin”.

I did work in this area to permit fine grained access control to items (and pages/widgets), hence I did have a partially working concept.

Best,
Łukasz

Hi there - amazing that this is being worked on :slight_smile:
Is there any way to know about progress on the work you have been doing on roles?
Or anything another dev completely new to openhab can help with?

Yes actually I have finish to manage the roles and groups with the Karaf console, now I manage the roles and the groups with the UI. I am currently implementing the role-based access control model for openHAB users for my master thesis. Once I’m done, I plan to create a post to explain exactly what I did and maybe try to do a pull request if my code is very clean.

Thank you,

Nicolas Gennart.

1 Like