Skip to main content

Permissions & Access Control

Complete guide to the permission system, permission wrappers, and role-based access control in AquaGen Web Application.


Overview

AquaGen implements a Role-Based Access Control (RBAC) system to manage user permissions and feature access across the application.

Key Features:

  • 33+ permission types for granular control
  • Permission wrapper components for UI elements
  • Permission controller for programmatic checks
  • Group permissions for batch assignments
  • Super user override capabilities
  • Service-based permissions

Components:

  • Permission Enums: All available permissions
  • Permission Controller: Business logic for permission checks
  • Permission Wrapper: React component for conditional rendering
  • Permission Popup: Browser permission dialogs

Permission Architecture

Core Files

FilePurposeLocation
permissions.jsPermission definitionslibs/shared/src/enums/permissions.js
PermissionController.jsPermission logiclibs/shared/src/controller/permission/PermissionController.js
PermissionWrapper.jsxReact wrapper componentlibs/components/src/permissionWrapper/PermissionWrapper.jsx
PopupPermission.jsxBrowser popup dialoglibs/components/src/permissionPopup/PopupPermission.jsx

Data Flow


All Available Permissions

Permission Enums

Location: libs/shared/src/enums/permissions.js:1-33

Core Permissions

PermissionPurposeUsage
DEFAULTDefault user accessAssigned if no permissions specified
SUPER_USERAdmin accessGrants access to all features (except disabled ones)

Feature Permissions

Dashboard & Monitoring

PermissionFeatureDescription
LANDING_PAGEDashboardMain landing page access
WATER_MONITORINGMonitoringWater data monitoring (always accessible)
DAILY_SUMMARYDaily SummaryDaily consumption summary

Water Management

PermissionFeatureDescription
WATER_BALANCEWater BalanceWater balance graphs and analysis
WATER_NEUTRALITYWater NeutralitySustainability tracking
WATER_RATIOWater RatioWater usage ratios

Water Indexes

PermissionFeatureDescription
RWIRain Water IndexRainwater harvesting index
RWI_EFFICIENCY_SAVINGSRWI SavingsRWI efficiency and savings display
UWIUrban Water IndexUrban water usage index
UWI_DASHBOARDUWI DashboardSpecial UWI dashboard view
UWI_SHOW_ENERGY_SAVINGUWI EnergyEnergy savings in UWI
WRIWater Risk IndexWater risk assessment
WRI_MACRO_LEVELWRI MacroMacro-level WRI view
WRI_MICRO_LEVELWRI MicroMicro-level WRI view
GWIGround Water IndexGround water monitoring

SCADA & HMI

PermissionFeatureDescription
SCADA_EDITORSCADA EditorCreate/edit SCADA diagrams
SCADA_VIEWERSCADA ViewerView SCADA diagrams
WEB_HMIWeb HMIHuman-Machine Interface

AI & Analytics

PermissionFeatureDescription
AQUAGPTAquaGPTAI-powered insights and chat
NLABSAquaGen LabsExperimental features

Alerts & Notifications

PermissionFeatureDescription
ALERTSAlertsAlert viewing and management
ENERGY_ALERTEnergy AlertsEnergy-specific alerts

Account Management

PermissionFeatureDescription
ACCOUNT_SETTINGSAccount SettingsUser account settings
MANUAL_NODE_ENTRYManual EntryManual data entry for nodes

Energy & Efficiency

PermissionFeatureDescription
EFFICIENCYEfficiencyEfficiency metrics (always accessible)
EFFICENCYEfficiency (typo)Duplicate for backward compatibility
DISABLE_ENERGY_TOGGLE_BUTTONEnergy ToggleDisable energy toggle (negative permission)

Reports

PermissionFeatureDescription
NOT_GENERAL_REPORTReport AccessExclude from general reports

Group Permissions

PermissionIncludesDescription
GROUP_OPERATIONSLATEST_DATAGroup of operational permissions

Permission Controller

Methods

Location: libs/shared/src/controller/permission/PermissionController.js

getAllUserPermissions()

Gets all permissions for a user, including expanded group permissions.

Signature:

getAllUserPermissions(loginData)

Parameters:

  • loginData (Object): User login data from authentication

Returns:

  • Array<string>: Array of permission strings

Logic:

  1. Extract service permissions (from loginData.services)
  2. Check if permissions array exists
  3. If no permissions, return ['DEFAULT']
  4. Process each permission:
    • If starts with GROUP_, expand group permissions
    • Otherwise, add individual permission
  5. Return formatted array

Example:

import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';

const userPermissions = PermissionController.getAllUserPermissions(loginData);
// Returns: ['LANDING_PAGE', 'WATER_BALANCE', 'AQUAGPT', ...]

Code Reference: libs/shared/src/controller/permission/PermissionController.js:3-26


isPermitted()

Checks if a user has a specific permission.

Signature:

isPermitted(tag, loginData, ignoreSuperUser = false)

Parameters:

  • tag (string): Permission to check
  • loginData (Object): User login data
  • ignoreSuperUser (boolean): Whether to ignore SUPER_USER privilege (default: false)

Returns:

  • boolean: true if permitted, false otherwise

Special Rules:

  1. SUPER_USER Override:

    • SUPER_USER has access to ALL features
    • Exceptions:
      • Permissions starting with DISABLE_
      • ACCOUNT_SETTINGS
      • UWI_DASHBOARD
  2. Always Accessible:

    • WATER_MONITORING - Always returns true
    • EFFICENCY - Always returns true
  3. Ignore Super User:

    • When ignoreSuperUser = true, super user privilege is ignored
    • Useful for specific permission checks

Example:

import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';

// Check if user can access AquaGPT
const canAccessAquaGPT = PermissionController.isPermitted(
Permissions.allPermission.AQUAGPT,
loginData
);

if (canAccessAquaGPT) {
// Show AquaGPT feature
}

// Check permission without super user override
const hasExactPermission = PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
loginData,
true // Ignore super user
);

Code Reference: libs/shared/src/controller/permission/PermissionController.js:28-48


Permission Wrapper Component

Overview

PermissionWrapper is a React component that conditionally renders children based on user permissions.

Location: libs/components/src/permissionWrapper/PermissionWrapper.jsx

Props

PropTypeDefaultDescription
tagstringrequiredPermission to check
childrenReactNoderequiredContent to render if permitted
negatebooleanfalseReverse the permission check
ignoreSuperUserbooleanfalseIgnore super user privilege

Basic Usage

Show content if user has permission:

import PermissionWrapper from '@aquagen-mf-webapp/components/permissionWrapper/PermissionWrapper';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';

function MyComponent() {
return (
<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<AquaGPTButton />
</PermissionWrapper>
);
}

Result: <AquaGPTButton /> only renders if user has AQUAGPT permission.


Negate Mode

Hide content if user has permission:

<PermissionWrapper
tag={Permissions.allPermission.UWI_DASHBOARD}
negate={true}
>
<StandardDashboard />
</PermissionWrapper>

Result: <StandardDashboard /> only renders if user does NOT have UWI_DASHBOARD permission.


Ignore Super User

Check exact permission, ignoring super user:

<PermissionWrapper
tag={Permissions.allPermission.SCADA_EDITOR}
ignoreSuperUser={true}
>
<ScadaEditorButton />
</PermissionWrapper>

Result: <ScadaEditorButton /> only renders if user has exact SCADA_EDITOR permission, even if they're a super user.


Real-World Examples

Example 1: Dashboard View Toggle

File: libs/dashboard/src/DashboardPage.jsx:173-181

<Container sx={{ pt: 2 }}>
{/* Show standard dashboard if user does NOT have UWI_DASHBOARD */}
<PermissionWrapper
tag={Permissions.allPermission.UWI_DASHBOARD}
negate
>
<BuildDashboardPage />
</PermissionWrapper>

{/* Show UWI dashboard if user HAS UWI_DASHBOARD */}
<PermissionWrapper tag={Permissions.allPermission.UWI_DASHBOARD}>
<BuildUWIDashboardPage />
</PermissionWrapper>
</Container>

Logic: Users see either standard OR UWI dashboard, not both.


Example 2: Navigation Menu Items

<PermissionWrapper tag={Permissions.allPermission.SCADA_EDITOR}>
<MenuItem onClick={() => navigate('/scadaEditor')}>
SCADA Editor
</MenuItem>
</PermissionWrapper>

<PermissionWrapper tag={Permissions.allPermission.SCADA_VIEWER}>
<MenuItem onClick={() => navigate('/scadaViewer')}>
SCADA Viewer
</MenuItem>
</PermissionWrapper>

Result: Menu items only appear if user has respective permissions.


Example 3: Feature Buttons

<PermissionWrapper tag={Permissions.allPermission.MANUAL_NODE_ENTRY}>
<AppButton
value="Manual Entry"
onClick={handleManualEntry}
/>
</PermissionWrapper>

<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<AppButton
value="Water Balance"
onClick={handleWaterBalance}
/>
</PermissionWrapper>

Example 4: Disable Features

<PermissionWrapper
tag={Permissions.allPermission.DISABLE_ENERGY_TOGGLE_BUTTON}
negate
>
<ToggleButton
value="energy"
onChange={handleEnergyToggle}
/>
</PermissionWrapper>

Result: Toggle button hidden if user has DISABLE_ENERGY_TOGGLE_BUTTON permission.


Component Code

import { useContext } from 'react';
import If from '@aquagen-mf-webapp/components/logical/If';
import IfNot from '@aquagen-mf-webapp/components/logical/IfNot';
import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';

function PermissionWrapper({ tag, children, negate = false, ignoreSuperUser = false }) {
const appStore = useContext(AppStoreContext);

if (negate) {
return (
<IfNot
condition={PermissionController.isPermitted(tag, appStore.loginData, ignoreSuperUser)}
>
{children}
</IfNot>
);
}

return (
<If condition={PermissionController.isPermitted(tag, appStore.loginData, ignoreSuperUser)}>
{children}
</If>
);
}

export default PermissionWrapper;

Code Reference: libs/components/src/permissionWrapper/PermissionWrapper.jsx:1-27


Programmatic Permission Checks

Using PermissionController Directly

For non-JSX scenarios (e.g., inside functions, conditions):

import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';
import { useContext } from 'react';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';

function MyComponent() {
const appStore = useContext(AppStoreContext);

const handleAction = () => {
// Check permission programmatically
if (PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
appStore.loginData
)) {
// User has permission - proceed
openScadaEditor();
} else {
// User lacks permission - show error
showPermissionError();
}
};

return <button onClick={handleAction}>Edit SCADA</button>;
}

Multiple Permission Checks

const appStore = useContext(AppStoreContext);

// Check multiple permissions
const hasWaterFeatures =
PermissionController.isPermitted(Permissions.allPermission.WATER_BALANCE, appStore.loginData) &&
PermissionController.isPermitted(Permissions.allPermission.WATER_NEUTRALITY, appStore.loginData);

const hasAnyWaterIndex =
PermissionController.isPermitted(Permissions.allPermission.RWI, appStore.loginData) ||
PermissionController.isPermitted(Permissions.allPermission.UWI, appStore.loginData) ||
PermissionController.isPermitted(Permissions.allPermission.WRI, appStore.loginData);

Getting All User Permissions

import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';

function DebugPermissions() {
const appStore = useContext(AppStoreContext);
const allPermissions = PermissionController.getAllUserPermissions(appStore.loginData);

return (
<div>
<h3>Your Permissions:</h3>
<ul>
{allPermissions.map(permission => (
<li key={permission}>{permission}</li>
))}
</ul>
</div>
);
}

Login Data Structure

Permission Data in Login Response

When user logs in, permissions are received in the response:

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"loginData": {
"userId": "12345",
"name": "John Doe",
"email": "user@example.com",
"services": [
{
"categoryId": "WATER_MONITORING",
"serviceName": "Water Monitoring"
},
{
"categoryId": "ENERGY",
"serviceName": "Energy Management"
}
],
"permissions": [
"SUPER_USER",
"LANDING_PAGE",
"WATER_BALANCE",
"AQUAGPT",
"SCADA_VIEWER",
"WRI",
"GROUP_OPERATIONS"
]
}
}

Accessing Permissions in Components

import { useContext } from 'react';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';

function MyComponent() {
const appStore = useContext(AppStoreContext);

// Access raw permissions
const rawPermissions = appStore.loginData?.permissions || [];

// Access services
const userServices = appStore.loginData?.services || [];

console.log('User permissions:', rawPermissions);
console.log('User services:', userServices);
}

Permission Patterns & Best Practices

1. Component-Level Protection

Wrap entire components:

import PermissionWrapper from '@aquagen-mf-webapp/components/permissionWrapper/PermissionWrapper';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';

function App() {
return (
<div>
<PublicComponent />

<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<AquaGPTPage />
</PermissionWrapper>

<PermissionWrapper tag={Permissions.allPermission.SCADA_EDITOR}>
<ScadaEditorPage />
</PermissionWrapper>
</div>
);
}

2. Element-Level Protection

Wrap buttons, links, or UI elements:

<Box>
<Typography>Dashboard Actions</Typography>

<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<Button>View Water Balance</Button>
</PermissionWrapper>

<PermissionWrapper tag={Permissions.allPermission.MANUAL_NODE_ENTRY}>
<Button>Manual Entry</Button>
</PermissionWrapper>
</Box>

3. Conditional Rendering Logic

Combine with other conditions:

<PermissionWrapper tag={Permissions.allPermission.ALERTS}>
{hasActiveAlerts && (
<AlertBanner alerts={activeAlerts} />
)}
</PermissionWrapper>

4. Negative Permissions

Show content when permission is NOT present:

{/* Show basic features if NOT super user */}
<PermissionWrapper tag={Permissions.allPermission.SUPER_USER} negate>
<BasicFeatures />
</PermissionWrapper>

{/* Show admin features if IS super user */}
<PermissionWrapper tag={Permissions.allPermission.SUPER_USER}>
<AdminFeatures />
</PermissionWrapper>

5. Multiple Wrappers

Nest wrappers for AND logic:

<PermissionWrapper tag={Permissions.allPermission.SCADA_EDITOR}>
<PermissionWrapper tag={Permissions.allPermission.SUPER_USER}>
<AdminScadaFeatures />
</PermissionWrapper>
</PermissionWrapper>

Result: Only renders if user has BOTH permissions.


6. Route Protection

Protect entire routes:

import PermissionWrapper from '@aquagen-mf-webapp/components/permissionWrapper/PermissionWrapper';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';

const routes = [
{
path: '/dashboard',
element: (
<PermissionWrapper tag={Permissions.allPermission.LANDING_PAGE}>
<DashboardPage />
</PermissionWrapper>
)
},
{
path: '/aquagpt',
element: (
<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<AquaGPTPage />
</PermissionWrapper>
)
}
];

Super User Behavior

What is a Super User?

A user with the SUPER_USER permission has access to ALL features in the application.

Characteristics:

  • Bypasses most permission checks
  • Gets access to all features automatically
  • Cannot be restricted from most features

Super User Exceptions

Super user does NOT automatically grant access to:

1. Disabled Features

Permissions starting with DISABLE_ are respected:

// Super user CANNOT access if DISABLE_ENERGY_TOGGLE_BUTTON is set
<PermissionWrapper tag={Permissions.allPermission.DISABLE_ENERGY_TOGGLE_BUTTON} negate>
<EnergyToggleButton />
</PermissionWrapper>

2. Account Settings

Super user must have explicit ACCOUNT_SETTINGS permission:

// Super user needs explicit permission
<PermissionWrapper tag={Permissions.allPermission.ACCOUNT_SETTINGS}>
<AccountSettingsPage />
</PermissionWrapper>

3. UWI Dashboard

Super user must have explicit UWI_DASHBOARD permission:

// Super user needs explicit permission
<PermissionWrapper tag={Permissions.allPermission.UWI_DASHBOARD}>
<UWIDashboardPage />
</PermissionWrapper>

Ignoring Super User

Use ignoreSuperUser prop to check exact permissions:

<PermissionWrapper
tag={Permissions.allPermission.SCADA_EDITOR}
ignoreSuperUser={true}
>
<ScadaEditorFeature />
</PermissionWrapper>

Result: Even super users must have exact SCADA_EDITOR permission.


Group Permissions

What are Group Permissions?

Group permissions allow assigning multiple permissions at once.

Definition: libs/shared/src/enums/permissions.js:35-37

const groupPermission = {
GROUP_OPERATIONS: [allPermission.LATEST_DATA]
};

How Group Permissions Work

When a user has a group permission:

  1. Permission starts with GROUP_
  2. PermissionController expands it to individual permissions
  3. All group members are added to user's permissions

Example:

User has:

permissions: ['GROUP_OPERATIONS']

Expands to:

formattedPermissions: ['LATEST_DATA']

Adding Group Permissions

To create new group:

  1. Edit libs/shared/src/enums/permissions.js
  2. Add to groupPermission object:
const groupPermission = {
GROUP_OPERATIONS: [allPermission.LATEST_DATA],
GROUP_WATER_FEATURES: [
allPermission.WATER_BALANCE,
allPermission.WATER_NEUTRALITY,
allPermission.WATER_RATIO
],
GROUP_SCADA: [
allPermission.SCADA_EDITOR,
allPermission.SCADA_VIEWER
]
};

Browser Permission Popup

PopupPermission Component

Purpose: Display dialog when browser blocks popups or redirects.

Location: libs/components/src/permissionPopup/PopupPermission.jsx

Usage:

import PopupPermission from '@aquagen-mf-webapp/components/permissionPopup/PopupPermission';

function MyComponent() {
const [popupOpen, setPopupOpen] = useState(false);

const handleOpenNewTab = () => {
const newWindow = window.open('/report', '_blank');

if (!newWindow) {
// Popup blocked - show dialog
setPopupOpen(true);
}
};

return (
<>
<button onClick={handleOpenNewTab}>Open Report</button>

<PopupPermission
open={popupOpen}
handleClose={() => setPopupOpen(false)}
/>
</>
);
}

Dialog Content:

  • Title: "Pop-ups and Redirection Blocked"
  • Message: "Please update the popup setting to allow new tab to open."
  • Action: "Cancel" button

Code Reference: libs/components/src/permissionPopup/PopupPermission.jsx:11-31


Testing Permissions

Test Different Permission Levels

1. Super User:

{
"permissions": ["SUPER_USER"]
}

Result: Access to all features

2. Basic User:

{
"permissions": ["LANDING_PAGE", "WATER_MONITORING"]
}

Result: Limited feature access

3. No Permissions:

{
"permissions": []
}

Result: Only DEFAULT permission

4. Group Permissions:

{
"permissions": ["GROUP_OPERATIONS"]
}

Result: Expands to group members


Debug Permission State

import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';

function DebugPermissions() {
const appStore = useContext(AppStoreContext);

// Get all permissions
const allPerms = PermissionController.getAllUserPermissions(appStore.loginData);

// Check specific permissions
const checks = {
isSuperUser: allPerms.includes('SUPER_USER'),
hasAquaGPT: PermissionController.isPermitted(
Permissions.allPermission.AQUAGPT,
appStore.loginData
),
hasScadaEditor: PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
appStore.loginData
)
};

console.log('All permissions:', allPerms);
console.log('Permission checks:', checks);

return <div>{JSON.stringify(checks, null, 2)}</div>;
}

Common Patterns

Pattern 1: Feature Toggle

<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<WaterBalanceFeature />
</PermissionWrapper>

Pattern 2: Conditional Navigation

const navigate = useNavigate();
const appStore = useContext(AppStoreContext);

const handleNavigation = (route, permission) => {
if (PermissionController.isPermitted(permission, appStore.loginData)) {
navigate(route);
} else {
showErrorMessage('Access Denied');
}
};

Pattern 3: Dynamic Menu

const menuItems = [
{ label: 'Dashboard', permission: Permissions.allPermission.LANDING_PAGE },
{ label: 'AquaGPT', permission: Permissions.allPermission.AQUAGPT },
{ label: 'SCADA Editor', permission: Permissions.allPermission.SCADA_EDITOR }
];

return (
<Menu>
{menuItems.map(item => (
<PermissionWrapper key={item.label} tag={item.permission}>
<MenuItem>{item.label}</MenuItem>
</PermissionWrapper>
))}
</Menu>
);

Pattern 4: Button Visibility

<Stack direction="row" spacing={2}>
<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<Button>Water Balance</Button>
</PermissionWrapper>

<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<Button>AquaGPT</Button>
</PermissionWrapper>

<PermissionWrapper tag={Permissions.allPermission.MANUAL_NODE_ENTRY}>
<Button>Manual Entry</Button>
</PermissionWrapper>
</Stack>

Error Handling

Permission Denied

const appStore = useContext(AppStoreContext);

const handleProtectedAction = () => {
if (!PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
appStore.loginData
)) {
// Show error
alert('You do not have permission to access this feature');
return;
}

// Proceed with action
performAction();
};

Missing Login Data

const appStore = useContext(AppStoreContext);

if (!appStore.loginData) {
// User not logged in
navigate('/login');
return null;
}

// Safe to check permissions
const hasPermission = PermissionController.isPermitted(
Permissions.allPermission.AQUAGPT,
appStore.loginData
);

Complete Permission List

All 33 Permissions

DEFAULT                          // Default user
SUPER_USER // Admin access
GROUP_OPERATIONS // Operations group
LANDING_PAGE // Dashboard
ALERTS // Alerts
WATER_BALANCE // Water balance
DAILY_SUMMARY // Daily summary
WATER_NEUTRALITY // Water neutrality
AQUAGPT // AquaGPT
RWI // Rain Water Index
UWI // Urban Water Index
GWI // Ground Water Index
UWI_DASHBOARD // UWI Dashboard
SCADA_EDITOR // SCADA Editor
SCADA_VIEWER // SCADA Viewer
WRI // Water Risk Index
WRI_MACRO_LEVEL // WRI Macro
WRI_MICRO_LEVEL // WRI Micro
DISABLE_ENERGY_TOGGLE_BUTTON // Disable energy toggle
UWI_SHOW_ENERGY_SAVING // UWI energy savings
RWI_EFFICIENCY_SAVINGS // RWI efficiency
EFFICIENCY // Efficiency
EFFICENCY // Efficiency (typo)
WATER_MONITORING // Water monitoring
MANUAL_NODE_ENTRY // Manual entry
NOT_GENERAL_REPORT // Report exclusion
WATER_RATIO // Water ratio
ENERGY_ALERT // Energy alerts
ACCOUNT_SETTINGS // Account settings
WEB_HMI // Web HMI
NLABS // AquaGen Labs

Next Steps


Last Updated: February 2026 Maintained By: AquaGen Development Team