# Anonim Geziler Güvenlik Açığı Düzeltmesi

## 🔴 Sorun

### Güvenlik Açığı
Önceki RLS politikaları, `user_id IS NULL` olan tüm anonim gezilere herkesin erişmesine izin veriyordu:

```sql
-- ❌ ESKİ VE GÜVENSİZ
USING (is_public = true OR auth.uid() = user_id OR user_id IS NULL);
```

Bu politika şu sorunlara yol açıyordu:
1. **Herkes başkasının anonim gezisini görebilir, düzenleyebilir ve silebilirdi**
2. **Anonim geziler sahipsiz kalıyordu** - kullanıcı giriş yaptıktan sonra gezisi kayboluyordu
3. **Veri tabanı kirliliği** - eski anonim geziler hiç temizlenmiyordu

### Etki
- Gizlilik ihlali: Kullanıcıların anonim gezileri herkese açık
- Veri kaybı: Login sonrası kullanıcı kendi gezisini bulamıyor
- Performans: Sahipsiz geziler birikerek veritabanını şişiriyor

---

## ✅ Çözüm

### 1. Token Tabanlı Erişim Sistemi

Her anonim gezi için benzersiz bir token oluşturulur ve localStorage'da saklanır:

```typescript
// Gezi oluşturulurken
const anonymousToken = crypto.randomUUID();
localStorage.setItem(`trip_token_${tripId}`, anonymousToken);
```

### 2. Güvenli RLS Politikaları

Yeni politikalar sadece token sahibinin erişimine izin verir:

```sql
-- ✅ YENİ VE GÜVENLİ
CREATE POLICY "Seyahatleri görüntüleme"
  ON trips FOR SELECT
  USING (
    is_public = true 
    OR user_id = auth.uid()
  );
```

Anonim geziler için özel RPC fonksiyonları:
- `update_anonymous_trip(trip_id, token, updates)` - Token ile güncelleme
- `delete_anonymous_trip(trip_id, token)` - Token ile silme
- `get_anonymous_trip(trip_id, token)` - Token ile okuma

### 3. Otomatik Ownership Transfer

Kullanıcı giriş yaptığında, anonim gezisi otomatik olarak hesabına bağlanır:

```typescript
// AuthContext.tsx - onAuthStateChange
if (event === 'SIGNED_IN') {
  const currentTripId = localStorage.getItem('currentTripId');
  if (currentTripId) {
    await tripsApi.claimAnonymousTrip(currentTripId);
  }
}
```

### 4. Otomatik Temizlik

7 günden eski anonim geziler otomatik olarak silinir:

```sql
CREATE FUNCTION cleanup_old_anonymous_trips()
RETURNS INTEGER
AS $$
  DELETE FROM trips
  WHERE user_id IS NULL
  AND created_at < NOW() - INTERVAL '7 days'
$$;
```

---

## 📋 Değişiklikler

### Database Migrations

#### `00053_add_anonymous_trip_token_system.sql`
- `trips` tablosuna `anonymous_token` sütunu eklendi
- Unique index oluşturuldu
- Güvenli RLS politikaları eklendi
- RPC fonksiyonları oluşturuldu:
  - `claim_anonymous_trip(trip_id, token)` - Ownership transfer
  - `update_anonymous_trip(trip_id, token, updates)` - Token ile güncelleme
  - `delete_anonymous_trip(trip_id, token)` - Token ile silme
  - `get_anonymous_trip(trip_id, token)` - Token ile okuma
  - `cleanup_old_anonymous_trips()` - Eski gezileri temizle

#### `00054_fix_anonymous_trip_rls_policies.sql`
- RLS politikalarını optimize etti
- Application layer'da token kontrolü için yapı oluşturdu

### Frontend Değişiklikleri

#### `src/db/api.ts`

**`tripsApi.create()`**
```typescript
// Anonim kullanıcı için token oluştur
if (!user) {
  anonymousToken = crypto.randomUUID();
  tripData = { ...trip, user_id: null, anonymous_token: anonymousToken };
  localStorage.setItem(`trip_token_${data.id}`, anonymousToken);
}
```

**`tripsApi.update()`**
```typescript
// Anonim kullanıcı için RPC kullan
if (!user) {
  const token = localStorage.getItem(`trip_token_${tripId}`);
  return await supabase.rpc('update_anonymous_trip', {
    trip_id_param: tripId,
    token_param: token,
    updates: updates
  });
}
```

**`tripsApi.delete()`**
```typescript
// Anonim kullanıcı için RPC kullan
if (!user) {
  const token = localStorage.getItem(`trip_token_${tripId}`);
  await supabase.rpc('delete_anonymous_trip', {
    trip_id_param: tripId,
    token_param: token
  });
  localStorage.removeItem(`trip_token_${tripId}`);
}
```

**Yeni Fonksiyonlar:**
- `claimAnonymousTrip(tripId)` - Anonim geziyi kullanıcıya transfer et
- `cleanupOldAnonymousTrips()` - Eski anonim gezileri temizle
- `canAccessTrip(tripId)` - Geziye erişim kontrolü

#### `src/contexts/AuthContext.tsx`

```typescript
// Login sonrası otomatik ownership transfer
if (event === 'SIGNED_IN') {
  const currentTripId = localStorage.getItem('currentTripId');
  if (currentTripId) {
    await tripsApi.claimAnonymousTrip(currentTripId);
  }
}
```

#### `src/types/trip-ui.ts`

```typescript
export interface TripDataRaw {
  // ...
  anonymous_token?: string; // Yeni alan
}
```

---

## 🔒 Güvenlik Özellikleri

### 1. Token Güvenliği
- **UUID v4** kullanılır (128-bit rastgele)
- **localStorage**'da saklanır (sadece aynı origin erişebilir)
- **Tek kullanımlık**: Transfer sonrası token silinir

### 2. RLS Koruması
- Kayıtlı kullanıcılar sadece kendi gezilerini görebilir
- Anonim geziler sadece token ile erişilebilir
- Public geziler herkese açık

### 3. Veri Temizliği
- 7 günden eski anonim geziler otomatik silinir
- Orphan data birikmesi önlenir

### 4. Ownership Transfer
- Login sonrası otomatik transfer
- Token doğrulaması ile güvenli transfer
- Transfer sonrası token temizlenir

---

## 🧪 Test Senaryoları

### Senaryo 1: Anonim Kullanıcı
1. ✅ Kullanıcı giriş yapmadan gezi oluşturur
2. ✅ Token localStorage'a kaydedilir
3. ✅ Kullanıcı geziyi düzenleyebilir
4. ✅ Kullanıcı geziyi silebilir
5. ✅ Başka bir tarayıcıdan geziye erişilemez

### Senaryo 2: Login Sonrası Transfer
1. ✅ Anonim kullanıcı gezi oluşturur
2. ✅ Kullanıcı giriş yapar
3. ✅ Gezi otomatik olarak kullanıcıya transfer edilir
4. ✅ Token temizlenir
5. ✅ Gezi "Seyahat Planlarım"da görünür

### Senaryo 3: Güvenlik Testi
1. ✅ Kullanıcı A anonim gezi oluşturur
2. ✅ Kullanıcı B aynı trip_id ile erişmeye çalışır
3. ✅ Erişim reddedilir (token yok)
4. ✅ Kullanıcı B geziyi düzenleyemez
5. ✅ Kullanıcı B geziyi silemez

### Senaryo 4: Temizlik
1. ✅ 7 günden eski anonim gezi oluşturulur
2. ✅ `cleanup_old_anonymous_trips()` çağrılır
3. ✅ Eski gezi silinir
4. ✅ Yeni anonim geziler korunur

---

## 📊 Performans

### Öncesi
- Tüm anonim geziler herkese açık
- N+1 query problemi
- Gereksiz veri transferi

### Sonrası
- Token kontrolü ile hızlı erişim
- RPC fonksiyonları ile optimize edilmiş sorgular
- Otomatik temizlik ile veritabanı boyutu kontrolü

---

## 🚀 Kullanım

### Anonim Gezi Oluşturma
```typescript
const trip = await tripsApi.create({
  title: 'Kapadokya Gezim',
  destination: 'Kapadokya',
  // ...
});
// Token otomatik oluşturulur ve kaydedilir
```

### Anonim Gezi Güncelleme
```typescript
await tripsApi.update(tripId, {
  title: 'Yeni Başlık'
});
// Token otomatik kontrol edilir
```

### Anonim Gezi Silme
```typescript
await tripsApi.delete(tripId);
// Token otomatik kontrol edilir ve temizlenir
```

### Manuel Ownership Transfer
```typescript
const success = await tripsApi.claimAnonymousTrip(tripId);
if (success) {
  console.log('Gezi başarıyla transfer edildi');
}
```

### Eski Gezileri Temizleme (Admin)
```typescript
const deletedCount = await tripsApi.cleanupOldAnonymousTrips();
console.log(`${deletedCount} adet eski gezi temizlendi`);
```

---

## ⚠️ Önemli Notlar

1. **Token Kaybı**: Kullanıcı localStorage'ı temizlerse token kaybolur ve geziye erişemez
2. **Tarayıcı Değişimi**: Farklı tarayıcıda token olmadığı için geziye erişilemez
3. **7 Gün Sınırı**: Anonim geziler 7 gün sonra otomatik silinir
4. **Public Geziler**: Public yapılan geziler token olmadan da görüntülenebilir (ama düzenlenemez)

---

## 🔄 Migration Sırası

1. `00053_add_anonymous_trip_token_system.sql` - Token sistemi ve RPC fonksiyonları
2. `00054_fix_anonymous_trip_rls_policies.sql` - RLS politikalarını optimize et

---

## 📝 Sonuç

Bu güvenlik düzeltmesi ile:
- ✅ Anonim geziler artık güvenli
- ✅ Kullanıcılar giriş sonrası gezilerini kaybetmiyor
- ✅ Veritabanı temiz kalıyor
- ✅ Performans optimize edildi
- ✅ Kullanıcı deneyimi iyileştirildi
