nonblocking / mashroom

Mashroom Server, an Integration Platform for Microfrontends
https://www.mashroom-server.com
MIT License
52 stars 5 forks source link

How to retrieve the user's roles in a ReactJS plugin application integrated with the Mashroom portal? #118

Open atans96 opened 2 months ago

atans96 commented 2 months ago
import {
  MashroomPortalAppService,
  MashroomPortalAppSetup,
  MashroomPortalAppUser,
  MashroomPortalClientServices,
  MashroomPortalMessageBus,
  MashroomPortalProxyPaths,
} from '@mashroom/mashroom-portal/type-definitions'
import React, { ReactNode, createContext, useCallback, useContext } from 'react'

type MashroomContextProps = {
  messageBus: MashroomPortalMessageBus
  portalAppService: MashroomPortalAppService
  appConfig: any
  bffBasePath: string
  user: MashroomPortalAppUser
  appId: string
  proxyPaths: MashroomPortalProxyPaths
  portalAppSetup: MashroomPortalAppSetup
  portalClientServices: MashroomPortalClientServices
  getRemoteUserPrivateTopic: () => string
}
type MashroomProviderProps = {
  children: ReactNode
  portalClientServices: MashroomPortalClientServices
  portalAppSetup: MashroomPortalAppSetup
}

const MashroomContext = createContext({} as MashroomContextProps)

export const useMashroom = () => useContext(MashroomContext)

export const MashroomProvider = ({ children, portalAppSetup, portalClientServices }: MashroomProviderProps) => {
  const { appConfig, proxyPaths, user, appId } = portalAppSetup
  const { messageBus, portalAppService } = portalClientServices || {}

  const bffBasePath = proxyPaths.bff

  console.log('MashroomProvider MashroomContext.', MashroomContext)

  /**
   * This is a custom function to get the user's private topic. The reason of using this is
   * the default getRemoteUserPrivateTopic provided by Mashroom is using the username as the
   * topic, but instead of using username, our application uses user_uid
   *
   * @returns string - user's private topic
   *
   * @throws {Error} if user_uid is not defined in keycloak
   * */
  const getRemoteUserPrivateTopic = useCallback(() => {
    if (!user.extraData.user_uid) throw new Error('user_uid is not defined in keycloak')

    return `userID/${user.extraData.user_uid}`
  }, [user.extraData.user_uid])

  return (
    <MashroomContext.Provider
      value={{
        messageBus,
        portalAppService,
        appConfig,
        bffBasePath,
        user,
        appId,
        proxyPaths,
        portalAppSetup,
        portalClientServices,
        getRemoteUserPrivateTopic,
      }}
    >
      {children}
    </MashroomContext.Provider>
  )
}
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

import type { MashroomPortalAppPluginBootstrapFunction } from "@mashroom/mashroom-portal/type-definitions";
import { MashroomProvider } from "@predictintel/cybersmart-react-library";

export const bootstrap: MashroomPortalAppPluginBootstrapFunction = (
  portalAppHostElement,
  portalAppSetup,
  clientServices
) => {
  const root = ReactDOM.createRoot(portalAppHostElement);
  root.render(
    <React.StrictMode>
      <MashroomProvider
        portalAppSetup={portalAppSetup}
        portalClientServices={clientServices}
      >
        <App />
      </MashroomProvider>
    </React.StrictMode>
  );

  return Promise.resolve({
    willBeRemoved: () => {
      root.unmount();
    },
  });
};
import type { MashroomPortalAppPluginBootstrapFunction } from "@mashroom/mashroom-portal/type-definitions";

// This allows lazy load of actual bootstrap.
// It is required to support microfrontend using Mashroom and Webpack Module Federation
const bootstrap: MashroomPortalAppPluginBootstrapFunction = async (
  portalAppHostElement,
  portalAppSetup,
  clientServices,
) => {
  try {
    const bootstrapModule = await import("./bootstrap");
    const mashroomBootstrap = bootstrapModule.bootstrap;
    mashroomBootstrap(portalAppHostElement, portalAppSetup, clientServices);
  } catch (error) {
    console.error("Error importing the module:", error);
  }
};

(global as any).reactUserMgtBootstrap = bootstrap;

I am developing a ReactJS plugin application (let's call it "plugin-app2") that will be integrated with the Mashroom portal web application. In this plugin application, I need to check the user's role to conditionally show or hide certain functionalities based on their permissions.

Is there a way to retrieve the roles assigned to the currently logged-in user within the Mashroom portal? This information would allow me to control the visibility of features in my ReactJS plugin application based on the user's role and permissions.

If there is a method or API provided by the Mashroom portal to access the user's role information, could someone please guide me on how to implement it in my ReactJS application?

Maybe this?

portalClientServices.portalAdminService.getExistingRoles()

Any help or suggestions would be greatly appreciated.

nonblocking commented 2 months ago

One of the design goals of Mashroom was to make the Apps portable and maximally reusable, therefore it is not possible to directly check some roles (which may be different from server to server). Instead, you can define permissions which are mapped to roles in the metadata. The App itself only receives a map of "permission name" -> boolean.

It works like this:

{
    "mashroom": {
        "plugins": [
            {
              // ...
                "defaultConfig": {
                   // ...
                    "rolePermissions": {
                        "allowedTodoSomethingSpecial": ["Role2", "Role3"]
                    }
                }
            }
        ]
    }
}

And in the App you can check if the user has the permission "allowedTodoSomethingSpecial" like this:

const bootstrap: MashroomPortalAppPluginBootstrapFunction = (portalAppHostElement, portalAppSetup, clientServices) => {
    const {appConfig: {markdownMessage, pingButtonLabel}, user} = portalAppSetup;

    if (user.permissions.allowedTodoSomethingSpecial) {
        // ...
    }

}

You could then change the mapping for every server instance in the "plugins" section of the server config.