Create a new UIResource to manipulate the roles between the client and the server

Hi everyone,

I am implementing actually the role-based access control for OpenHAB users.

I am currently implementing the methods to get/put roles and groups from/to the server side. To do this, I need to manage the openhab-core project and the openhab-webui project.

I created the accessControl package at the location org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/accessControl and created the AccessControlRessource.java class as shown below:

package org.openhab.core.io.rest.core.internal.accessControl;

import java.io.IOException;
import java.security.Principal;
import java.util.Optional;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.*;
import javax.ws.rs.core.*;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.GroupRegistry;
import org.openhab.core.auth.Role;
import org.openhab.core.auth.RoleRegistry;
import org.openhab.core.auth.UserRegistry;
import org.openhab.core.io.rest.*;
import org.openhab.core.io.rest.auth.VerifyToken;
import org.openhab.core.io.rest.core.internal.item.ItemResource;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;

@Component
@JaxrsResource
@JaxrsName(AccessControlRessource.PATH_ACCESS_CONTROL)
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
@JSONRequired
@Path(AccessControlRessource.PATH_ACCESS_CONTROL)
// see https://docs.swagger.io/swagger-core/v2.1.12/apidocs/io/swagger/v3/oas/annotations/tags/Tag.html
@Tag(name = AccessControlRessource.PATH_ACCESS_CONTROL)
@NonNullByDefault
public class AccessControlRessource implements RESTResource {
    /** The URI path to this resource */
    public static final String PATH_ACCESS_CONTROL = "accessControl";

    private final Logger logger = LoggerFactory.getLogger(ItemResource.class);

    private final UserRegistry userRegistry;
    private final RoleRegistry roleRegistry;

    @Activate
    public AccessControlRessource(final @Reference UserRegistry userRegistry,
            final @Reference RoleRegistry roleRegistry) {

        this.userRegistry = userRegistry;
        this.roleRegistry = roleRegistry;

    }

    @GET
    @PermitAll
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(operationId = "getTest", summary = "test function", responses = {
            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = String.class))),
            @ApiResponse(responseCode = "404", description = "Item not found") })
    public Response getTest(final @Context UriInfo uriInfo, final @Context HttpHeaders httpHeaders) {

        System.out.println("That works!!!");
        System.out.println(principal.getName());

        return Response.ok("IT works").build();
    }

    /**
     *
     * @param
     * @return
     */
    @GET
    @RolesAllowed({ Role.USER, Role.ADMIN })
    @Path("/role")
    @Produces(MediaType.TEXT_PLAIN)
    @Operation(operationId = "getRoleTest", summary = "test role function", responses = {
            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = String.class))),
            @ApiResponse(responseCode = "404", description = "Item not found") })
    public Response getRoleTest() {
        // get item
        System.out.println("It works");
        // we cannot use JSONResponse.createResponse() bc. MediaType.TEXT_PLAIN
        // return JSONResponse.createResponse(Status.OK, item.getState().toString(), null);
        return Response.ok("It works!!!").build();
    }
}

For now, I’m just trying to invoke the getTest function or the getRoleTest function with the /rest/accessControl and /rest/accessControl/role path respectively on the client side, i.e. in the project openhab-webui, by simply calling :

this.$oh.api.get('/rest/accessControl')
this.$oh.api.get('/rest/accessControl/role')

But this does not work and the client responds with the message 406 Not Acceptable.

I don’t know what I missed, because when I call this.$oh.api.get('/rest/items') for example on the client side, it works fine. The function in the ItemResource.java to get the items is well invoked. The ItemResource.java is located at org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/item/ItemResource.java on the server side, i.e. openhab-core.

The function this.$oh.api.get() is located at bundles/org.openhab.ui/web/src/js/openhab/api.js in the project openhab-webui and implements the methods of HTTP protocol.

Maybe I did something wrong with the javax annotation. I took inspiration from other classes like ItemResource.java at org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal\item but I don’t know what I did wrong. Is there another class I need to modify to invoke the functions of AccessControlRessource.java ?

What did I miss to be able to invoke the function in the AccessControlRessource.java class by calling the get method of the HTTP protocol in the client side (openhab-webui)?

What I want to do next is to manipulate the roles between the client(openhab-webui) and the server (openhab-core ) with the HTTP protocol like the items is manage.

I hope I have made myself clear,

Thank you,

Nicolas Gennart.

I can suggest you just two things.

  1. Check if your custom rest endpoint is listed at http://localhost:8080/rest/. In its output you should see something like that:
{"type":"accessControl","url":"http://127.0.0.1:8080/rest/accessControl"}
  1. If it doesn’t work then (comparing some of my custom resources), I see only one difference in my code:
@Component(service = { RESTResource.class, GatewayResource.class })
// where yours is just
@Component

There is also a bunch of conditions related simply to fact if you run code you made. In other words if the build openhab distribution really includes your code. Given description you made it seems that your endpoint is simply not pulled in. If you are not sure verify bundle headers and its build timestamp. If you can’t find any information about build timestamp just open openhab core jar with Recaf or jd-gui. These are a decompilers which will allow you to verify if your code come into build.

Best,
Łukasz

1 Like

Thanks Lukasz for these explanations, finally I succeeded, what I had done worked.

Thank you,

Nicolas.