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.