BETA

User Permissions

The Merchant Center allows configuring permissions based on a user’s team. Teams belong to an organization and users can be member of multiple teams.

See User Permission Concepts for more information about the relationships between users, teams, and organizations.

Types of permissions

The Merchant Center exposes different types of user permissions that are bound to a project and can be used within a Custom Application code. For example, to restrict access to a view, to prevent updating or deleting certain resources, etc.

General permissions

General permissions are bound to a resource and grant either view or manage access.

A manage access always implies view access as well.

A general permission is represented as a string, with the resource name prefixed by View or Manage. The following resource names are currently supported:

  • Products
  • Categories
  • Customers
  • CustomerGroups
  • Orders
  • ProductDiscounts
  • CartDiscounts
  • DiscountCodes
  • ProjectSettings
  • ProductTypes
  • DeveloperSettings

Some examples are: ViewProducts, ManageOrders, etc.

Action rights

Action rights can be used to refine the general permissions. For example, manage access to products can be limited to exclude publishing.

An action right is represented as an object with the shape { group: string, name: string }. The group relates to the permission while the name is the action right itself. Currently the following action rights for the group: 'products' exist:

  • PublishProducts
  • UnpublishProducts
  • AddPrices
  • EditPrices
  • DeletePrices
  • DeleteProducts
  • AddProducts

Using configured permissions within a Custom Application

The @commercetools-frontend/permissions package should be used to implement and apply the user permissions for the specific parts of the Custom Applications. The package exposes React components and React hooks to make it easy to work with it:

  • useIsAuthorized()
  • injectAuthorized()
  • <RestrictedByPermissions>

useIsAuthorized(options)

A React hook that evaluates the demanded permissions against the user permissions and returns a boolean value. Only the demanded permissions need to be explicitly provided, the user permissions are implicitly read from the application context.

Supported named arguments

Usage

import { PERMISSIONS } from './constants/permissions.js';
const ExampleComponent = () => {
const canManageProducts = useIsAuthorized({
demandedPermissions: [PERMISSIONS.ManageProducts],
});
return <span>{ canManageProducts ? 'Authorized' : 'Not authorized' }</span>;
};

injectAuthorized(permissions, [options])

A React higher-order component that evaluates the demanded permissions against the user permissions and injects the result as a component prop (default to isAuthorized).

Supported arguments

  • demandedPermissions: an array of demanded general permissions.
  • options (optional)
  • propName: (optional) the name of the injected prop (default to isAuthorized).

Usage

const InputField = props => (
<Input
disabled={!props.isAuthorized}
/>
);
export default injectAuthorized([PERMISSIONS.ViewProducts])(
InputField
);

<RestrictedByPermissions>

A React component that evaluates the demanded permissions against the user permissions and renders the result as a function with the named argument isAuthorized.

Supported props

  • permissions: an array of demanded general permissions.
  • actionRights: (optional) an array of demanded action rights.
  • unauthorizedComponent: (optional) a React component instance to be rendered in case the evaluation of the permissions returns false. This happens the permissions do not match.
  • render: (optional) a render prop function that should return a React node. The function is called with the named boolean argument isAuthorized.
  • children: (optional) a render prop function that should return a React node. The function is called with the named boolean argument isAuthorized.

Usage

<RestrictedByPermissions permissions={[PERMISSIONS.ManageOrders]} unauthorizedComponent={Unauthorized}>
<MyAuthorizedComponent />
</RestrictedByPermissions>
<RestrictedByPermissions permissions={[PERMISSIONS.ManageOrders]}
{({ isAuthorized }) => <button disabled={!isAuthorized}>My button</button>}
</RestrictedByPermissions>

Best practices

Using a file with constants

It is recommended to store general permissions and action rights used by your application into a permissions.js file. This helps to avoid repeating permission names across your code base and preventing typographical errors while giving an overview of used permissions in an application easily.

export const ACTION_RIGHTS = {
PublishProducts: { group: 'products', name: 'PublishProducts' },
};
export const PERMISSIONS = {
ManageOrders: 'ManageOrders'
}

Evaluate permissions separately

It is recommended to evaluate each permission separately even if the respective APIs allow specifying an array of general permissions and action rights. Combining different permissions in one hook or component usage can become hard to reason about. Splitting and later joining resulting booleans helps to keep the implications of permissions easier to understand.

// Instead of
const canManageProductsAndViewOrders = useIsAuthorized({
demandedPermissions: [PERMISSIONS.ViewOrders, PERMISSIONS.ManageProducts],
});
// Apply the hook twice...
const canManageProducts = useIsAuthorized({
demandedPermissions: [PERMISSIONS.ManageProducts],
});
const canViewOrders = useIsAuthorized({
demandedPermissions: [PERMISSIONS.ViewOrders],
});
// ...and compose the resulting booleans
const canManageProductsAndViewOrders = canManageProducts && canViewOrders;

Co-locate all evaluations in a shared module

It is recommended to create a shared module containing a React hook, component, or higher-order component to share all permission evaluations across your application.

  • It helps not to spread similar configuration across multiple components.
  • Decreases the likelihood of silent bugs due to false configurations.
  • Allows changes in your configuration to easily propagate through your application.

For example, define a React hook usePermissions that includes evaluations of different permissions used across the application.

const usePermissions = () => {
const canManageProducts = useIsAuthorized({
demandedPermissions: [PERMISSIONS.ManageProducts],
});
const canViewOrders = useIsAuthorized({
demandedPermissions: [PERMISSIONS.ViewOrders],
});
const canPublishProducts = useIsAuthorized({
demandedPermissions: [PERMISSIONS.ManageOrders],
demandedActionRights: [ACTION_RIGHTS.PublishProducts],
});
return {
canManageProducts,
canViewOrders,
canPublishProducts,
}
}

This React hook can then be used across an entire Custom Application.

const InputField = props => {
const { canManageProducts } = usePermissions();
return (
<Input
disabled={!canManageProducts}
/>
);
};