Otomatisasi hak untuk permintaan penghapusan

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

Regulasi Perlindungan Data Umum (GDPR) adalah peraturan Eropa tentang perlindungan data dan privasi.Ini memberikan individu hak untuk meminta penghapusan data pribadi mereka, yang dikenal sebagai hak untuk menghapus.Jika Anda menyimpan informasi Pengenal Pribadi (PII) dari pengguna Anda, seperti User ID mereka, Anda harus mematuhi persyaratan GDPR dengan menghapus informasi ini setelah menerima permintaan pengguna.

Alih-alih menangani permintaan secara manual, Anda dapat mengatur webhook dan menggunakan bot dalam aplikasi pesan pihak ketiga untuk mengotomatisasi prosesnya.Sebagai penyimpanan data menjadi cara paling umum untuk menyimpan data PII, tutorial ini memberikan contoh tentang cara membuat bot di Guilded atau Discord yang menggunakan Open Cloud API untuk penyimpanan data untuk menghapus data PII sebagai solusi otomatisasi.

Alur kerja

Setelah menyelesaikan tutorial ini, Anda harus dapat membuat program khusus lokal yang menyederhanakan penanganan permintaan hak untuk menghapus dari pengguna.Alur kerja untuk proses ini adalah sebagai berikut:

  1. Dukungan Roblox menerima hak untuk permintaan hapus dari pengguna.
  2. Webhook Roblox diaktifkan, berisi ID Pengguna dan daftar ID Tempat Mulai untuk pengalaman yang telah mereka bergabung dalam payload.
  3. Bot Anda mendengarkan pemberitahuan webhook ini, memverifikasi keasliannya, dan menggunakan Open Cloud API untuk penyimpanan data untuk menghapus data PII yang disimpan di penyimpanan data.
  4. Bot merespon pesan webhook di Discord atau Guilded dengan status penghapusan.

Konfigurasi webhook dengan integrasi pihak ketiga

Sebelum membuat bot, buat server dengan integrasi webhook pada aplikasi pesan pihak ketiga.Kemudian gunakan server untuk mengonfigurasi webhook di Dasbor Kreator.

Menyiapkan server

Langkah berikut menunjukkan cara mengatur server menggunakan Guilded atau Discord.

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

Konfigurasi webhook di Roblox

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

  • Tambahkan URL server Guilded atau Discord sebagai URL Webhook .
  • Terdiri dari sebuah kunci khusus Rahasia .Meskipun rahasia opsional untuk menyelesaikan konfigurasi, Anda harus menyertakan satu untuk mencegah aktor jahat menyamar sebagai Roblox dan menghapus data Anda.Untuk informasi lebih lanjut tentang penggunaan rahasia, lihat Verifikasi keamanan webhook.
  • Pilih Hak untuk Permintaan Penghapusan di bawah Pemicu .

Anda dapat menguji webhook menggunakan tombol Balasan Tes untuk melihat apakah Anda menerima pemberitahuan di saluran #umum server Anda dari Roblox.Jika Anda tidak menerima notifikasi, coba lagi atau periksa pengaturan server Anda untuk memecahkan masalah kesalahan.

Example notification on Guilded

Konfigurasi bot

Setelah Anda menambahkan webhook, gunakan untuk mengonfigurasi bot dengan langkah berikut:

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

    • CtrlS pada Windows.
    • S pada Mac.
  2. Pilih server Anda untuk menerima hak untuk menerima pemberitahuan hapus.

  3. Perluas daftar di bawah Rumah server dan pilih Kelola Bot .

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

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

  6. Pilih bagian API pada halaman konfigurasi bot.

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

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

Buat kunci API Cloud unit

Untuk mengizinkan bot pihak ketiga Anda mengakses penyimpanan data Anda untuk menyimpan data PII pengguna, buat kunci API Cloud Terbuka yang dapat mengakses pengalaman Anda dan menambahkan izin Hapus Entri penyimpanan data untuk penghapusan data.Jika Anda menggunakan penyimpanan data tertata untuk menyimpan PII, Anda juga perlu menambahkan izin Tulis dari penyimpanan data tertata.Setelah selesai, salin dan simpan kunci API di lokasi yang aman untuk digunakan di langkah selanjutnya.

Dapatkan identifikasi pengalaman dan tempat

Untuk bot menemukan data PII yang diminta oleh pengguna untuk dihapus, dapatkan identifikator berikut dari semua pengalaman yang Anda rencanakan untuk menggunakan bot:

  • ID Alam Semesta , identifikator unik dari pengalaman Anda.
  • ID Tempat Mulai , identifikasi unik dari tempat mulai pengalaman.

Untuk mendapatkan identifikator ini:

  1. Navigasikan ke Dashboard Pencipta.

  2. Pasang mouse di atas thumbnail pengalaman, klik tombol dan pilih Copy Universe ID dan Copy Start Place ID , masing-masing.

Tambahkan skrip

Setelah Anda menyelesaikan pengaturan webhook, bot, dan kunci API untuk penyimpanan data, tambahkan ke skrip yang menerapkan logika otomatisasi bot.Contoh berikut menggunakan Python 3:

  1. Pasang perpustakaan Python menggunakan perintah berikut:

    Pasang Perpustakaan

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

    bot_config.py

    BOT_TOKEN = ""
    OPEN_CLOUD_API_KEY = ""
    ROBLOX_WEBHOOK_SECRET = ""
    # Kamus dari ID tempat awal ke
    # (ID alam semesta, daftar (nama penyimpan data, scope, dan kunci unit)) untuk
    # Penyimpanan Data Standard
    # Data pengguna yang disimpan di bawah entri ini akan dihapus
    STANDARD_DATA_STORE_ENTRIES = {
    # ID Tempat Mulai
    111111111: (
    # ID Alam Semesta
    222222222,
    [
    ("StandardDataStore1", "Scope1", "Key1_{user_id}"),
    ("StandardDataStore1", "Scope1", "Key2_{user_id}"),
    ("StandardDataStore2", "Scope1", "Key3_{user_id}")
    ]
    ),
    33333333: (
    444444444,
    [
    ("StandardDataStore3", "Scope1", "Key1_{user_id}")
    ]
    )
    }
    # Kamus dari ID tempat awal ke
    # (ID alam semesta, daftar (nama penyimpan data, scope, dan kunci unit)) untuk
    # Toko Data Teratur
    # Data pengguna yang disimpan di bawah entri ini akan dihapus
    ORDERED_DATA_STORE_ENTRIES = {
    111111111: (
    222222222,
    [
    ("OrderedDataStore1", "Scope2", "Key4_{user_id}")
    ]
    )
    }
    data_stores_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
    message_parser.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 putar ulang dalam jendela 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
    # Memeriksa tanda tangan
    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 tangan 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):
    # Parses 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):
    # Memeriksa dan memverifikasi pesan
    user_id, start_place_ids = message_parser.parse_message(message)
    if not user_id or not start_place_ids:
    return
    # Menghapus data toko data standar pengguna
    [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 toko data yang diperintahkan 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.BOT_TOKEN)
    if __name__ == "__main__":
    run()
  3. Pada file bot_config.py untuk konfigurasi utama bot:

    1. Tetapkan 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 atur saat mengkonfigurasi webhook di Dasbor Kreator.
    4. Dalam STANDARD_DATA_STORE_ENTRIES dan ORDERED_DATA_STORE_ENTRIES kamus untuk menemukan penyimpanan data masing-masing rekaman untuk dihapus:
      1. Tambahkan ID Tempat Awal yang disalin sebagai kunci.
      2. Tambahkan ID Univers sebagai elemen pertama dari nilai tuple.
      3. Ganti elemen kedua dari tuple dengan nama, scope, nama kunci entri, dan ID Pengguna terkait dari penyimpanan data Anda.Jika Anda menggunakan skema data yang berbeda, modifikasi untuk sesuai dengan skema data Anda sendiri sesuai.
  4. Jalankan perintah berikut untuk menjalankan bot:

    Jalankan Bot Guilded

    python3 guilded_bot.py
  5. Bot kemudian mulai mendengarkan dan memverifikasi webhook Roblox untuk hak menghapus Permintaan dan panggilan ke titik akhir Open Cloud untuk menghapus tokodata yang sesuai.

Uji

Anda dapat membuat dan menjalankan pesan pengujian untuk memverifikasi bahwa program khusus Anda dapat menangani dengan benar permintaan hapus dan menghapus data PII:

  1. Kirim permintaan HTTP POST ke server webhook Guilded atau Discord dengan tubuh permintaan 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 rahasia webhook:

    1. Hasilkan Roblox-Signature dengan menerapkan enkripsi HMAC-SHA256 ke kunci unitwebhook Anda.
    2. Tetapkan waktu saat ini menggunakan timestamp UTC dalam detik sebagai Timestamp .
  3. Rakit bersama 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 mengodekan pesan dengan rahasia Anda.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/aset/webhook/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}