Mengotomatisasi Hak Hapus Permintaan

*Konten ini diterjemahkan menggunakan AI (Beta) dan mungkin mengandung kesalahan. Untuk melihat halaman ini dalam bahasa Inggris, klik di sini.

Undang-undang Perlindungan Data Umum (GDPR) adalah undang-undang Eropa tentang perlindungan data dan privasi. Ini memberi individu hak untuk meminta penghapusan data pribadi mereka, yang dikenal sebagai hak untuk menghapus. Jika Anda menyimpan setiap Informasi Pribadi yang Dapat Dipeng

Alih-alih menangani permintaan secara manual, Anda dapat mengatur panggilan webhook dan menggunakan bot dalam aplikasi pesan lain untuk secara otomatis mengotomatisasi proses ini. Karena simpan data adalah cara paling umum untuk menyimpan data PII, tutorial ini memberikan contoh tentang cara mengatur bot dalam Guild

Alur kerja

Setelah menyelesaikan tutorial ini, Anda harus dapat membuat program khusus yang berjalan lokal yang secara otomatis menangani permintaan hak penghapusan dari pengguna. Flow kerja untuk proses ini sebagai berikut:

  1. Dukungan Roblox menerima permintaan penghapusan dari pengguna.
  2. Pelat Salib Roblox diaktifkan, berisi ID Pengguna dan daftar Start Place ID untuk pengalaman yang mereka bergabung dalam payload.
  3. Bot Anda mendengarkan notifikasi webhook ini, memverifikasi keaslian mereka, dan menggunakan Open Cloud API for data stores untuk menghapus data PII yang disimpan di data store.
  4. Bot menanggapi pesan webhook di Discord atau Guilded dengan status penghapusan.

Mengkonfigurasi Webhook dengan Integrasi Pihak Ketiga

Sebelum menciptakan bot, konfigurasikan server dengan integrasi webhook di aplikasi pesan pihak ketiga. Kemudian gunakan server untuk mengkonfigurasi webhook di Dasbor Kreator.

Mengatur Server

Langkah-langkah berikut menunjukkan cara mengatur server menggunakan Guilded atau Discord.

  1. Buat server Guilded baru. Jika Anda tidak familiar dengan prosesnya, lihat Dukungan Guilded.
  2. Di bawah pengaturan Privasi , server diatur menjadi privat. Server secara otomatis membuat saluran pribadi #general sebagai channeldefault Anda.
  3. Buat integrasi webhook dengan server baru dan beri nama yang mudah Anda mengerti, seperti GDPR Hook. Jika Anda tidak familiar dengan proses ini, lihat Dukungan Guilded.
  4. Kopi URL webhook dan simpan di tempat yang aman. Hanya memungkinkan anggota tim yang dipercaya untuk mengaksesnya, karena menyebarkan URL dapat mengaktifkan aktor jahat untuk mengirim pesan palsu dan potensialmente menghapus data pengguna Anda.

Mengkonfigurasi Webhook di Roblox

Setelah mendapatkan URL server pihak ketiga, gunakan itu untuk konfigurasikan webhook di Dasbor Kreator. pastikan Anda melakukan pengaturan berikut:

  • Tambahkan URL server Guilded atau Discord sebagai URL Webhook .
  • Masukkan Rahasia khusus . Meskipun rahasia adalah pilihan untuk menyelesaikan konfigurasi, Anda harus meng包括 satu untuk mencegah aktor jahat menyamar Roblox dan menghapus data Anda. Untuk lebih banyak informasi tentang penggunaan rahasia, lihat Verifying Webhook Security.
  • Pilih Hak Hapus Permintaan di bawah Trigger .

Anda dapat menguji webhook menggunakan tombol Tester Balasan untuk melihat apakah Anda menerima notifikasi di saluran # general Roblox Anda. Jika Anda tidak menerima notifikasi, coba lagi atau periksa pengaturan server Anda untuk menyelesaikan masalah.

Example notification on Guilded

Mengkonfigurasi Bot

Setelah menambahkan webhook, gunakan itu untuk mengkonfigurasi bot dengan langkah berikut:

  1. Buka daftar Semua server dengan mengklik ikonnya atau gunakan pintasan cepat:

    • CtrlS di Windows.
    • S di Mac.
  2. Pilih server Anda untuk menerima pengingat pemusnahan.

  3. Jadilah daftar di bawah Beranda server dan pilih Manage Bots .

  4. Server secara otomatis membuat saluran pribadi # general sebagai channeldefault Anda.

  5. Klik tombol Buat Bot dan tambahkan nama bot. Guilded mengalihkan Anda ke halaman konfigurasi bot.

  6. Pilih bagian API di halaman konfigurasi bot.

  7. Di bawah bagian Token , klik tombol Menghasilkan Token .

  8. Simpan dan toko token yang dihasilkan di tempat yang aman.

Menciptakan Kunci API Cloud Open

Untuk memungkinkan bot pihak ketiga Anda untuk mengakses toko data Anda untuk menyimpan data PII pengguna, buat kunci Open Cloud API yang dapat mengakses pengalaman Anda dan menambahkan izin Menghapus Entri dari toko data untuk menyimpan data. Jika Anda menggunakan toko data y

Mendapatkan Identifier Pengalaman dan Tempat

Untuk membantu bot menemukan data PII yang diminta pengguna untuk dihapus, dapatkan identifikasi berikut dari semua pengalaman yang Anda niatkan untuk menggunakan bot untuk:

  • ID Alam Semesta, identifikator unik pengalaman Anda.
  • The Start Place ID , identitas unik dari tempat awal pengalaman.

Untuk mendapatkan identitas ini, buka halaman Karya di Dashboard Pencipta. Kemudian pilih pengalaman dan salinan ID Universe dan 2>Start Place ID2>.

Copy Universe ID and Copy Start Place ID options from Creator Dashboard

Menambahkan Skrip

Setelah Anda selesai mengatur webhook, bot, dan kunci API untuk penyimpanan data, tambahkan mereka ke skrip yang meng implementasikan logika otomatisasi bot. Contoh berikut menggunakan Python 3:

  1. Instal library Python menggunakan perintah berikut:

    Pasang Perpustakaan

    pip3 install discord
    pip3 install guilded.py==1.8.0
    pip3 install requests
    pip3 install urllib3==1.26.6
  2. Kopi dan simpan skrip berikut yang sesuai dengan berbagai bagian logika bot di direktori yang sama:

    bot_konfigurasi.py

    DISCORD_BOT_TOKEN = ""
    GUILDED_BOT_TOKEN = ""
    OPEN_CLOUD_API_KEY = ""
    ROBLOX_WEBHOOK_SECRET = ""
    # Daftar Start place ID untuk
    # (ID alam semesta, daftar (nama toko data, skala, dan kunci unit)) untuk
    # Toko Data Standar
    # Data pengguna yang disimpan di bawah entri ini akan dihapus
    STANDARD_DATA_STORE_ENTRIES = {
    # ID Tempat Mulai
    111111111: (
    # ID Universe
    222222222,
    [
    ("StandardDataStore1", "Scope1", "Key1_{user_id}"),
    ("StandardDataStore1", "Scope1", "Key2_{user_id}"),
    ("StandardDataStore2", "Scope1", "Key3_{user_id}")
    ]
    ),
    33333333: (
    444444444,
    [
    ("StandardDataStore3", "Scope1", "Key1_{user_id}")
    ]
    )
    }
    # Daftar Start place ID untuk
    # (ID alam semesta, daftar (nama toko data, skala, dan kunci unit)) untuk
    # Toko Data yang Dipesan
    # Data pengguna yang disimpan di bawah entri ini akan dihapus
    ORDERED_DATA_STORE_ENTRIES = {
    111111111: (
    222222222,
    [
    ("OrderedDataStore1", "Scope2", "Key4_{user_id}")
    ]
    )
    }
    数据_存储_api.py

    import requests
    import bot_config
    from collections import defaultdict
    """
    Calls Data Stores Open Cloud API to delete all entries for a user_id configured in
    STANDARD_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete.
    """
    def delete_standard_data_stores(user_id, start_place_ids):
    successes = defaultdict(list)
    failures = defaultdict(list)
    for owned_start_place_id in bot_config.STANDARD_DATA_STORE_ENTRIES:
    if owned_start_place_id not in start_place_ids:
    continue
    universe_id, universe_entries = bot_config.STANDARD_DATA_STORE_ENTRIES[owned_start_place_id]
    for (data_store_name, scope, entry_key) in universe_entries:
    entry_key = entry_key.replace("{user_id}", user_id)
    response = requests.delete(
    f"https://apis.roblox.com/datastores/v1/universes/{universe_id}/standard-datastores/datastore/entries/entry",
    headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY},
    params={
    "datastoreName": data_store_name,
    "scope": scope,
    "entryKey": entry_key
    }
    )
    if response.status_code in [200, 204]:
    successes[owned_start_place_id].append((data_store_name, scope, entry_key))
    else:
    failures[owned_start_place_id].append((data_store_name, scope, entry_key))
    return successes, failures
    """
    Calls Ordered Data Stores Open Cloud API to delete all entries for a user_id configured in
    ORDERED_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete.
    """
    def delete_ordered_data_stores(user_id, start_place_ids):
    successes = defaultdict(list)
    failures = defaultdict(list)
    for owned_start_place_id in bot_config.ORDERED_DATA_STORE_ENTRIES:
    if owned_start_place_id not in start_place_ids:
    continue
    universe_id, universe_entries = bot_config.ORDERED_DATA_STORE_ENTRIES[owned_start_place_id]
    for (data_store_name, scope, entry_key) in universe_entries:
    entry_key = entry_key.replace("{user_id}", user_id)
    response = requests.delete(
    f"https://apis.roblox.com/ordered-data-stores/v1/universes/{universe_id}/orderedDatastores/{data_store_name}/scopes/{scope}/entries/{entry_key}",
    headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY}
    )
    if response.status_code in [200, 204, 404]:
    successes[owned_start_place_id].append((data_store_name, scope, entry_key))
    else:
    failures[owned_start_place_id].append((data_store_name, scope, entry_key))
    return successes, failures
    pesan_sisip.py

    import time
    import hmac
    import hashlib
    import re
    import base64
    import bot_config
    """
    Parses received message for Roblox signature and timestamp, the footer is only set if you
    configured webhook secret
    """
    def parse_footer(message):
    if not message.embeds[0].footer or \
    not message.embeds[0].footer.text:
    return "", 0
    footer_match = re.match(
    r"Roblox-Signature: (.*), Timestamp: (.*)",
    message.embeds[0].footer.text
    )
    if not footer_match:
    return "", 0
    else:
    signature = footer_match.group(1)
    timestamp = int(footer_match.group(2))
    return signature, timestamp
    """
    Verifies Roblox signature with configured secret to check for validity
    """
    def validate_signature(message, signature, timestamp):
    if not message or not signature or not timestamp:
    return False
    # Mencegah serangan ulang dalam jendela waktu 300 detik
    request_timestamp_ms = timestamp * 1000
    window_time_ms = 300 * 1000
    oldest_timestamp_allowed = round(time.time() * 1000) - window_time_ms
    if request_timestamp_ms < oldest_timestamp_allowed:
    return False
    # Mengaktifkan tanda tangani
    timestamp_message = "{}.{}".format(timestamp, message.embeds[0].description)
    digest = hmac.new(
    bot_config.ROBLOX_WEBHOOK_SECRET.encode(),
    msg=timestamp_message.encode(),
    digestmod=hashlib.sha256
    ).digest()
    validated_signature = base64.b64encode(digest).decode()
    if signature != validated_signature:
    return False
    # Tanda tangani yang valid
    return True
    """
    Parses a received webhook messaged on Discord or Guilded. Extracts user ID, prevents replay attack
    based on timestamp received, and verifies Roblox signature with configured secret to check for
    validity.
    """
    def parse_message(message):
    # Menerima pesan untuk ID pengguna dan ID permainan
    if len(message.embeds) != 1 or \
    not message.embeds[0].description:
    return "", []
    description_match = re.match(
    r"You have received a new notification for Right to Erasure for the User Id: (.*) in " +
    r"the game\(s\) with Ids: (.*)",
    message.embeds[0].description
    )
    if not description_match:
    return "", []
    user_id = description_match.group(1)
    start_place_ids = set(int(item.strip()) for item in description_match.group(2).split(","))
    signature, timestamp = parse_footer(message)
    if validate_signature(message, signature, timestamp):
    return user_id, start_place_ids
    else:
    return "", []
    guilded_bot.py

    import guilded
    import json
    import bot_config
    import data_stores_api
    import message_parser
    def run():
    client = guilded.Client()
    @client.event
    async def on_ready():
    print(f"{client.user} is listening to Right to Erasure messages")
    """
    Handler for webhook messages from Roblox
    """
    @client.event
    async def on_message(message):
    # Mengambil dan menyediakan pesan
    user_id, start_place_ids = message_parser.parse_message(message)
    if not user_id or not start_place_ids:
    return
    # Menghapus data pengguna toko data standar
    [successes, failures] = data_stores_api.delete_standard_data_stores(user_id, start_place_ids)
    if successes:
    await message.reply(f"Deleted standard data stores data for " +
    f"user ID: {user_id}, data: {dict(successes)}")
    if failures:
    await message.reply(f"Failed to delete standard data stores data for " +
    f"user ID: {user_id}, data: {dict(failures)}")
    # Menghapus data yang dipesan menyimpan data pengguna
    [successes, failures] = data_stores_api.delete_ordered_data_stores(user_id, start_place_ids)
    if successes:
    await message.reply(f"Deleted ordered data stores data for " +
    f"user ID: {user_id}, data: {dict(successes)}")
    if failures:
    await message.reply(f"Failed to delete ordered data stores data for " +
    f"user ID: {user_id}, data: {dict(failures)}")
    client.run(bot_config.GUILDED_BOT_TOKEN)
    if __name__ == "__main__":
    run()
  3. Pada file bot_config.py untuk konfigurasi utama bot:

    1. Tetapkan DISCORD_BOT_TOKEN atau GUILDED_BOT_TOKEN ke token yang dihasilkan oleh bot Anda.
    2. Tetapkan OPEN_CLOUD_API_KEY sebagai kunci API yang Anda buat.
    3. Tetapkan ROBLOX_WEBHOOK_SECRET sebagai rahasia yang Anda tetapkan saat mengkonfigurasi webhook di Dasbor Kreator.
    4. Di STANDARD_DATA_STORE_ENTRIES dan ORDERED_DATA_STORE_ENTRIES典典 untuk menemukan data store masing-masing catatan untuk dihapus:
      1. Tambahkan Start Place ID Anda sebagai kunci.
      2. Tambahkan ID Universe sebagai elemen pertama dari nilai tuple.
      3. Ganti elemen kedua dari tuple dengan nama, scope, nama kunci entri, dan User ID yang terkait dari data store Anda. Jika Anda menggunakan schema data yang berbeda, modifikasi untuk menyesuaikan data script Anda sesuai.
  4. Jalankan perintah berikut untuk mengeksekusi bot:

    Jalankan Bot Guilded

    python3 guilded_bot.py
  5. Bot kemudian mulai mendengarkan dan memverifikasi Roblox webhook untuk hak menghapus permintaan dan panggilan Open Cloud untuk menghapus data yang sesuai.

Menguji

Anda dapat menciptakan dan mengeksekui pesan pengujian untuk memverifikasi bahwa program khusus Anda dapat menangani permintaan hak akses dengan benar dan menghapus data PII:

  1. Kirim permintaan HTTP POST ke server webhook Guilded atau Discord Anda dengan tubuhpermintaan berikut:

    Permintaan Contoh

    curl -X POST {serverUrl}
    -H 'Content-Type: application/json'
    -d '{
    "embeds":[{
    "title":"RightToErasureRequest",
    "description":"You have received a new notification for Right to Erasure for the User Id: {userIds} in the game(s) with Ids: {gameIds}",
    "footer":{
    "icon_url":"https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png",
    "text":"Roblox-Signature: {robloxSignature}, Timestamp: {timestamp}"
    }
    }]
    }'
  2. Jika Anda memiliki webhook rahasia:

    1. Hasilkan Roblox-Signature dengan menerapkan unitenkripsi HMAC-SHA256 ke kunci webhook rahasia Anda.
    2. Tetapkan waktu saat ini menggunakan UTC timestamp dalam detik sebagai Timestamp .
  3. Gabungkan description dalam format berikut:

    Description Field Format

    {Timestamp}. You have received a new notification for Right to Erasure for the User Id: {userId} in the game(s) with Ids: {gameIds}`.

    Sebagai contoh:

    Example Description Field

    1683927229. You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955

Program Anda harus dapat mengidentifikasi bahwa pesan Anda berasal dari sumber Roblox resmi karena Anda telah men-Encoding pesan dengan rahasia Anda. Itu kemudian harus menghapus data PII yang terkait dengan permintaan Anda.

Contoh Tubuh

{
"embeds": [
{
"title": "RightToErasureRequest",
"description": "You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955",
"footer": {
"icon_url": "https://create.roblox.com/ashboard/asset/webhook/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}