Skip to main content

Overview

MKing Admin implements a robust role-based access control (RBAC) system that controls user access to features and data throughout the application. The system uses permission slugs to enforce access rules at both the routing and UI levels.

Architecture

Permission Storage

Permissions are stored in Zustand state management and persisted through JWT authentication:
// src/store/authStore.ts
interface AuthState {
  user: User | null;
  permissions: string[];  // Array of permission slugs
  isAuthenticated: boolean;
  setAuth: (user: User, permissions: string[]) => void;
  clearAuth: () => void;
}

Permission Extraction

When a user logs in, their permissions are extracted from their assigned roles:
// src/auth/pages/LoginPage.tsx:34
const { data: userData } = await getInfoUser();
const perms = userData.roles.flatMap((role: any) =>
  role.permissions.map((p: any) => p.slug)
);
setAuth(userData, perms);

Permission System

Available Permissions

Permissions are organized by module using a hierarchical slug structure:
ModulePermission SlugDescription
Homehome.panelAccess to dashboard
Productsproducts.viewView products
Catalogscatalogs.manageManage product catalogs
Clientsclients.viewView client information
Quotationsquotations.admin.viewView admin quotations
Rolesroles.viewView and manage roles
Employeesemployees.viewView employees
Usersusers.viewManage system users
Calendarevents.viewAccess calendar
Permission slugs follow the pattern module.action (e.g., products.view, catalogs.manage)

Creating Permissions

Permissions are created through the Permissions Manager interface:
// src/pages/Roles/components/PermissionsManager.tsx:64
const handleAdd = async () => {
  if (!form.name || !form.slug || !form.module) {
    Swal.fire('Error', 'Nombre, Slug y Módulo son obligatorios', 'error');
    return;
  }
  
  await createPermission(form);
};
Permission Structure:
  • Name: Display name (e.g., “View Users”)
  • Slug: Unique identifier (e.g., users.view)
  • Module: Grouping category (e.g., “Usuarios”)
  • Description: Optional explanation

Role Management

Creating Roles

Roles group multiple permissions together and are assigned to users:
1

Define Role Information

Navigate to /roles/create and provide:
  • Name: Role display name
  • Slug: Unique identifier (cannot be changed after creation)
  • Description: Optional role description
2

Select Permissions

Permissions are grouped by module. You can:
  • Select individual permissions by checking them
  • Select all permissions in a module by checking the module header
  • The checkbox will show indeterminate state when some (but not all) module permissions are selected
3

Save Role

The role is created with the selected permissions and can be assigned to users

Role API Endpoints

// src/services/admin.service.tsx:105-109
export const getRoles = async () => await axios.get(`${apiUrl}/roles`);
export const getRole = async (id: number) => await axios.get(`${apiUrl}/roles/${id}`);
export const createRole = async (body: any) => await axios.post(`${apiUrl}/roles`, body);
export const updateRole = async (id: number, body: any) => await axios.put(`${apiUrl}/roles/${id}`, body);
export const deleteRole = async (id: number) => await axios.delete(`${apiUrl}/roles/${id}`);

Role Protection

The admin role cannot be deleted to prevent system lockout. This protection is enforced in the UI at src/pages/Roles/RolesPage.tsx:74

Permission Enforcement

Route Protection

Routes are protected using the ProtectedRoute component:
// src/router/AppRouter.tsx:68-74
const routeMaps = routeList.map(({ id, page, permission, path }: any) => (
  <Route
    key={id}
    element={<ProtectedRoute isAllowed={permissions.includes(permission) || permission === "auth"} />}
  >
    <Route path={path} element={<PageTransition>{page}</PageTransition>} />
  </Route>
));
The ProtectedRoute component redirects unauthorized users:
// src/router/ProtectedRoute.tsx:3-12
export const ProtectedRoute = ({ isAllowed, redirectTo = "/home", children }: any) => {
  if (!isAllowed) {
    return <Navigate to={redirectTo} replace />;
  }
  return children ? children : <Outlet />;
};
The sidebar menu is dynamically filtered based on user permissions:
// src/router/AppRouter.tsx:64-66
const routeList = MenuSidebar.filter(
  (item) => permissions.includes(item.permission) || item.permission === "auth"
);

Permission Checks in Components

Components can access permissions through the auth store:
import { useAuthStore } from "../../store/authStore";

const permissions = useAuthStore((state) => state.permissions);

// Check if user has specific permission
if (permissions.includes('products.view')) {
  // Show product features
}

Permission Groups Component

The PermissionGroup component displays permissions organized by module:
// src/pages/Roles/components/PermissionGroup.tsx:11-28
export const PermissionGroup = ({ moduleName, permissions, selectedPermissions, onTogglePermission, onToggleModule }) => {
  const allSelected = permissions.every(p => selectedPermissions.includes(p.id));
  const someSelected = permissions.some(p => selectedPermissions.includes(p.id));
  
  return (
    <Paper variant="outlined" sx={{ p: 2, height: '100%' }}>
      <FormControlLabel
        control={
          <Checkbox
            checked={allSelected}
            indeterminate={someSelected && !allSelected}
            onChange={() => onToggleModule(moduleName, permissions)}
          />
        }
        label={<Typography variant="subtitle1" fontWeight="bold">{moduleName}</Typography>}
      />
      {/* Individual permission checkboxes */}
    </Paper>
  );
};

Best Practices

Permission Naming

Use consistent naming: module.action pattern (e.g., users.view, products.create)

Granular Permissions

Create specific permissions for different actions (view, create, edit, delete) rather than broad access

Module Grouping

Group related permissions under the same module for easier management

Role Hierarchy

Design roles from least to most privileged (e.g., Viewer → Editor → Admin)

Common Workflows

Adding a New Protected Feature

  1. Create Permission: Use Permissions Manager to add a new permission slug
  2. Update MenuSidebar: Add the permission to the relevant menu item in src/core/MenuSidebar.tsx
  3. Protect Routes: Ensure the route uses ProtectedRoute with the permission check
  4. Update Roles: Assign the new permission to appropriate roles

Modifying User Access

  1. Navigate to Roles page (/roles)
  2. Edit the user’s assigned role
  3. Toggle permissions as needed
  4. Save the role - changes apply immediately to all users with that role
Users must log out and log back in for permission changes to take effect, as permissions are cached in the auth store during login.

Build docs developers (and LLMs) love