User Data Access: Implementing LGPD Art. 18

by Alex Johnson 44 views

In today's digital landscape, data privacy is paramount. Regulations like the Lei Geral de Proteção de Dados (LGPD) in Brazil mandate that users have the right to access their personal data stored by organizations. This article outlines the implementation of a feature that allows users to view their data, adhering to LGPD Art. 18, I and II.

Understanding LGPD and the Right of Access

The LGPD, Brazil's comprehensive data protection law, grants individuals several rights regarding their personal data. Among these, the right of access, as specified in Article 18, paragraphs I and II, empowers users to confirm whether an organization processes their data and to access that data. This means providing users with a clear and understandable view of all the information a company holds about them.

Implementing this right is not just about legal compliance; it's about building trust with your users. By being transparent about the data you collect and how you use it, you demonstrate a commitment to privacy and empower users to make informed decisions about their data.

Feature Description: User Data Visualization

This feature provides users with a centralized location to view all the data stored about them within the application. It's designed to be easily accessible and understandable, ensuring that users can readily exercise their right of access under the LGPD.

Access Path: Users can access this feature by navigating to Configurações → Privacidade → Ver Dados (Settings → Privacy → View Data).

Data to be Displayed

The following categories of data are included in the user data visualization:

  • Informações Pessoais (Personal Information): This includes basic user details such as email address, full name, and account creation date. This information is fundamental for identifying and contacting the user.
  • Contas OAuth (OAuth Accounts): A list of linked OAuth providers (e.g., Google, GitHub). This shows the user which third-party accounts are connected to their application account, enhancing transparency and control.
  • Mapas Natais (Natal Charts): A list containing metadata related to natal charts, including the person's name, birth date/time, and location. (This is specific to the application context mentioned in the original text).
  • Consentimentos (Consents): A history of terms and conditions accepted by the user. This provides a record of the user's agreements and demonstrates compliance with consent requirements.
  • Atividade Recente (Recent Activity): The last 20 actions performed by the user, also known as audit logs. This helps users understand their activity within the application and provides a means to detect any unauthorized access.

Implementation Details

This section details the backend and frontend implementation of the user data visualization feature.

Backend Implementation

The backend implementation focuses on retrieving and structuring the user's data from the database. The following describes the endpoint and data retrieval process.

Endpoint: GET /api/v1/privacy/my-data

The endpoint is designed to be secure and efficient, retrieving only the data relevant to the currently logged-in user.

@router.get("/my-data", response_model=UserDataView)
async def view_my_data(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Visualiza dados armazenados (LGPD Art. 18, I e II)."""
    
    # Buscar dados relacionados
    oauth_accounts = await db.execute(
        select(OAuthAccount).where(OAuthAccount.user_id == current_user.id)
    )
    
    birth_charts = await db.execute(
        select(BirthChart).where(BirthChart.user_id == current_user.id)
    )
    
    consents = await db.execute(
        select(UserConsent).where(UserConsent.user_id == current_user.id)
    )
    
    recent_activity = await db.execute(
        select(AuditLog)
        .where(AuditLog.user_id == current_user.id)
        .order_by(AuditLog.created_at.desc())
        .limit(20)
    )
    
    return {
        "user": {
            "email": current_user.email,
            "full_name": current_user.full_name,
            "created_at": current_user.created_at,
        },
        "oauth_accounts": [{
            "provider": acc.provider
        } for acc in oauth_accounts.scalars()],
        "birth_charts": [{
            "person_name": chart.person_name,
            "birth_datetime": chart.birth_datetime,
            "city": chart.city,
        } for chart in birth_charts.scalars()],
        "consents": [{
            "type": c.consent_type,
            "date": c.consent_date
        } for c in consents.scalars()],
        "recent_activity": [{
            "action": log.action,
            "date": log.created_at
        } for log in recent_activity.scalars()],
    }

Explanation:

  1. Authentication: The endpoint uses get_current_user to ensure that only authenticated users can access their data.
  2. Data Retrieval: The code retrieves data from various database tables (OAuthAccount, BirthChart, UserConsent, AuditLog) related to the user.
  3. Data Transformation: The retrieved data is transformed into a structured format suitable for the frontend.
  4. Response: The endpoint returns a JSON response containing the user's personal information, linked OAuth accounts, natal chart metadata, consent history, and recent activity.

Schema: apps/api/app/schemas/privacy.py

The UserDataView schema defines the structure of the JSON response returned by the endpoint.

class UserDataView(BaseModel):
    user: dict
    oauth_accounts: list[dict]
    birth_charts: list[dict]
    consents: list[dict]
    recent_activity: list[dict]

Frontend Implementation

The frontend implementation focuses on displaying the user's data in a readable and user-friendly format. Here are details of the React component.

Component: apps/web/src/components/settings/PrivacySettings.tsx

import { Eye } from 'lucide-react';
import { useState } from 'react';
import { privacyService } from '@/services/privacy';

export function ViewDataSection() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);

  const handleViewData = async () => {
    setLoading(true);
    try {
      const result = await privacyService.viewMyData();
      setData(result);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="p-4 border rounded">
      <div className="flex items-start justify-between">
        <div>
          <h3 className="font-semibold flex items-center gap-2">
            <Eye className="w-5 h-5" />
            Direito de Acesso
          </h3>
          <p className="text-sm text-gray-600 mt-1">
            Visualizar todos os dados que armazenamos sobre você.
          </p>
        </div>
        <button
          onClick={handleViewData}
          disabled={loading}
          className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
        >
          {loading ? 'Carregando...' : 'Ver Dados'}
        </button>
      </div>

      {data && (
        <div className="mt-4 bg-gray-50 p-4 rounded">
          <h4 className="font-semibold mb-2">Informações Pessoais</h4>
          <p>Email: {data.user.email}</p>
          <p>Nome: {data.user.full_name}</p>
          <p>Conta criada em: {new Date(data.user.created_at).toLocaleDateString()}</p>

          <h4 className="font-semibold mt-4 mb-2">Contas Vinculadas ({data.oauth_accounts.length})</h4>
          {data.oauth_accounts.map((acc, i) => (
            <p key={i}>• {acc.provider}</p>
          ))}

          <h4 className="font-semibold mt-4 mb-2">Mapas Natais ({data.birth_charts.length})</h4>
          {data.birth_charts.map((chart, i) => (
            <p key={i}>• {chart.person_name} - {chart.city}</p>
          ))}
        </div>
      )}
    </div>
  );
}

Explanation:

  1. Data Fetching: The handleViewData function calls the privacyService.viewMyData() function to fetch the user's data from the backend.
  2. Loading State: The loading state variable is used to display a loading indicator while the data is being fetched.
  3. Data Display: The component displays the user's data in a structured format, including personal information, linked OAuth accounts, and natal chart metadata. The toLocaleDateString() method is used to format the account creation date.

Acceptance Criteria

The following criteria must be met for the feature to be considered complete:

  • [ ] Endpoint /api/v1/privacy/my-data implemented.
  • [ ] Returns all data of the logged-in user.
  • [ ] Frontend displays data in a readable format.
  • [ ] Action recorded in audit log.
  • [ ] Unit tests in the backend.
  • [ ] Loading state during request.

Estimation

Time: 2-3 hours

Labels

enhancement, privacy, lgpd, backend, frontend

By implementing this feature, organizations can demonstrate their commitment to data privacy and comply with the requirements of the LGPD, building trust with their users and fostering a culture of transparency. Remember to always consult the official LGPD documentation for the most accurate and up-to-date information.