Authorization & Access Control
Role-based access control, permissions, and authorization guards for Smart Shelf.
Authorization & Access Control
This section covers the authorization architecture, role-based access control (RBAC), and permission management in Smart Shelf.
Role-Based Access Control (RBAC)
User Roles Hierarchy
// lib/auth/roles.ts
export enum UserRole {
ADMIN = 'admin',
MANAGER = 'manager',
STAFF = 'staff',
READONLY = 'readonly',
}
export const ROLE_HIERARCHY = {
[UserRole.ADMIN]: 4,
[UserRole.MANAGER]: 3,
[UserRole.STAFF]: 2,
[UserRole.READONLY]: 1,
};
Permission System
export const PERMISSIONS = {
// Product Management
'products:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF, UserRole.READONLY],
'products:create': [UserRole.ADMIN, UserRole.MANAGER],
'products:update': [UserRole.ADMIN, UserRole.MANAGER],
'products:delete': [UserRole.ADMIN],
// Inventory Management
'inventory:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF, UserRole.READONLY],
'inventory:adjust': [UserRole.ADMIN, UserRole.MANAGER],
'inventory:transfer': [UserRole.ADMIN, UserRole.MANAGER],
'inventory:count': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF],
// Order Management
'orders:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF, UserRole.READONLY],
'orders:create': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF],
'orders:update': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF],
'orders:approve': [UserRole.ADMIN, UserRole.MANAGER],
'orders:cancel': [UserRole.ADMIN, UserRole.MANAGER],
// Financial
'financial:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.READONLY],
'financial:reports': [UserRole.ADMIN, UserRole.MANAGER],
// User Management
'users:read': [UserRole.ADMIN],
'users:create': [UserRole.ADMIN],
'users:update': [UserRole.ADMIN],
'users:delete': [UserRole.ADMIN],
// System Administration
'system:settings': [UserRole.ADMIN],
'system:backup': [UserRole.ADMIN],
'system:audit': [UserRole.ADMIN],
} as const;
export function hasPermission(userRole: UserRole, permission: string): boolean {
const allowedRoles = PERMISSIONS[permission as keyof typeof PERMISSIONS];
return allowedRoles?.includes(userRole) ?? false;
}
export function requirePermission(user: User, permission: string): void {
if (!hasPermission(user.role, permission)) {
throw new Error(`Access denied: ${permission} permission required`);
}
}
export function hasHigherRole(userRole: UserRole, targetRole: UserRole): boolean {
return ROLE_HIERARCHY[userRole] > ROLE_HIERARCHY[targetRole];
}
Authorization Guards
Component-Level Authorization
// components/auth/role-guard.tsx
interface RoleGuardProps {
allowedRoles: UserRole[];
children: React.ReactNode;
fallback?: React.ReactNode;
}
export function RoleGuard({ allowedRoles, children, fallback }: RoleGuardProps) {
const { user } = useCurrentUser();
if (!user || !allowedRoles.includes(user.role)) {
return fallback || <UnauthorizedMessage />;
}
return <>{children}</>;
}
// Usage
<RoleGuard allowedRoles={[UserRole.ADMIN, UserRole.MANAGER]}>
<AdminPanel />
</RoleGuard>
Permission-Based Guards
// components/auth/permission-guard.tsx
interface PermissionGuardProps {
permission: string;
children: React.ReactNode;
fallback?: React.ReactNode;
}
export function PermissionGuard({ permission, children, fallback }: PermissionGuardProps) {
const { user } = useCurrentUser();
if (!user || !hasPermission(user.role, permission)) {
return fallback || null;
}
return <>{children}</>;
}
// Usage
<PermissionGuard permission="products:create">
<CreateProductButton />
</PermissionGuard>
API Route Authorization
// lib/auth/api-auth.ts
export function withAuth(
handler: (req: NextRequest, user: User) => Promise<NextResponse>,
requiredPermission?: string
) {
return async function(req: NextRequest) {
try {
// Validate authentication
const user = await validateAuth(req);
// Check permission if required
if (requiredPermission) {
requirePermission(user, requiredPermission);
}
return handler(req, user);
} catch (error) {
if (error.message.includes('token')) {
return NextResponse.json(
{ success: false, error: { code: 'UNAUTHORIZED', message: 'Invalid token' } },
{ status: 401 }
);
}
if (error.message.includes('Access denied')) {
return NextResponse.json(
{ success: false, error: { code: 'FORBIDDEN', message: error.message } },
{ status: 403 }
);
}
throw error;
}
};
}
// Usage
export const GET = withAuth(async (req, user) => {
// Handler with authenticated user
}, 'products:read');
Database-Level Authorization
Row Level Security (RLS) Policies
User-Based Data Isolation
-- Users can only access their own profile
CREATE POLICY "user_profile_isolation" ON user_profiles
FOR ALL USING (
auth.uid() = id
);
-- Users can only view profiles in their organization
CREATE POLICY "organization_profile_access" ON user_profiles
FOR SELECT USING (
EXISTS (
SELECT 1 FROM user_profiles up
WHERE up.id = auth.uid()
AND up.organization_id = user_profiles.organization_id
)
);
Role-Based Data Access
-- Admins can access all data
CREATE POLICY "admin_full_access" ON products
FOR ALL USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND role = 'admin'
)
);
-- Managers can access data in their warehouse
CREATE POLICY "manager_warehouse_access" ON inventory
FOR ALL USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid()
AND role IN ('admin', 'manager')
AND (warehouse_id = inventory.warehouse_id OR role = 'admin')
)
);
-- Staff can only read data in their warehouse
CREATE POLICY "staff_warehouse_read" ON inventory
FOR SELECT USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid()
AND warehouse_id = inventory.warehouse_id
)
);
Time-Based Access Control
-- Users can only modify recent records
CREATE POLICY "recent_modifications_only" ON stock_movements
FOR UPDATE USING (
created_at > NOW() - INTERVAL '24 hours'
AND created_by = auth.uid()
);
-- Prevent modification of finalized orders
CREATE POLICY "no_finalized_order_changes" ON sales_orders
FOR UPDATE USING (
status NOT IN ('shipped', 'delivered', 'cancelled')
);
Data Masking and Field-Level Security
-- Hide sensitive customer data from staff
CREATE POLICY "customer_data_privacy" ON customers
FOR SELECT USING (
CASE
WHEN EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND role IN ('admin', 'manager')
) THEN true
ELSE false
END
);
-- Create a view with masked data for staff
CREATE OR REPLACE VIEW customer_summary AS
SELECT
id,
name,
CASE
WHEN EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND role IN ('admin', 'manager')
) THEN email
ELSE REGEXP_REPLACE(email, '(.{2}).*(@.*)', '\1***\2')
END as email,
city,
country,
is_active
FROM customers;
Access Control Best Practices
Development Guidelines
- Always use principle of least privilege
- Implement defense in depth with multiple authorization layers
- Test authorization logic thoroughly
- Use consistent permission naming conventions
- Document all permission requirements
Security Checklist
- All API endpoints have proper authorization
- Database policies implemented for all tables
- Component-level guards in place
- Permission checks on sensitive operations
- Regular access reviews conducted
- Authorization testing automated
This authorization system provides fine-grained access control while maintaining flexibility and scalability for Smart Shelf operations.