Automatyzuj prawo do żądań usuwania

*Ta zawartość została przetłumaczona przy użyciu narzędzi AI (w wersji beta) i może zawierać błędy. Aby wyświetlić tę stronę w języku angielskim, kliknij tutaj.

Rozporządzenie ogólne o ochronie danych (RODO) jest europejskim rozporządzeniem dotyczącym ochrony danych i prywatność, poufność.Zapewnia osobom prawo do żądania usunięcia ich danych osobowych, znane jako prawo do usunięcia.Jeśli przechowujesz jakiekolwiek Osobowo identyfikujące informacje (dane osobowe) użytkowników, takie jak ich ID użytkownika, musisz przestrzegać wymogów RODO, usuwając te informacje po otrzymaniu prośbaużytkownika.

Zamiast obsługiwać żądania ręcznie, możesz skonfigurować webhook i użyć bota w aplikacji wysyłania wiadomości strony trzeciej, aby zautomatyzować proces.Ponieważ magazyny danych są najczęstszym sposobem przechowywania danych PII, ten samouczek dostarcza przykład na to, jak utworzyć bot w Guilded lub Discord, który wykorzystuje otwartą chmurę API dla magazynów danych, aby usunąć dane PII jako rozwiązanie automatyzacji.

Przepływ pracy

Po zakończeniu tego samouczka powinieneś być w stanie stworzyć lokalnie uruchamiany niestandardowy program, który zautomatyzuje obsługę żądań prawa do usuwania od użytkowników.Przepływ pracy dla tego procesu wygląda następująco:

  1. Wsparcie Roblox otrzymuje prawo do wniosku o usunięcie od użytkownika.
  2. Webhook Roblox jest uruchamiany, zawierający identyfikator użytkownika i listę identyfikatorów miejsc startu dla doświadczeń, do których dołączył w zapytaniu o wysyłkę.
  3. Twój bot słucha tych powiadomień o webhookach, weryfikuje ich autentyczność i wykorzystuje Otwartą chmurkową API do przechowywania danych, aby usunąć dane PII przechowywane w magazynach danych.
  4. Bot odpowiada na wiadomość webhooka w Discordzie lub Guilded ze statusem usunięcia.

Konfiguruj webhook z integracją stron trzecich

Zanim utworzysz bot, skonfiguruj serwer z integracją webhook na aplikacji wysyłania wiadomości strony trzeciej.Następnie użyj serwera, aby skonfigurować webhook na pulpicu nawigacyjnym twórcy.

Ustaw serwer

Następujące kroki pokazują, jak skonfigurować serwer za pomocą Guilded lub Discord.

  1. Utwórz nowy serwer Guilded. Jeśli nie znasz procesu, zobacz Wsparcie Guilded.
  2. W ustawieniach Prywatność , ustaw serwer na prywatne.Serwer automatycznie tworzy prywatny kanał #ogólny jako domyślny kanał.
  3. Stwórz integrację webhook z nowym serwerem i nadaj mu nazwę, którą możesz łatwo zrozumieć, taką jak GDPR Hook.Jeśli nie znasz procesu, zobacz Wsparcie Guilded.
  4. Skopiuj URL webhooka i przechowaj go w bezpiecznym miejsce.Pozwól tylko zaufanym członkom zespołu na uzyskanie do niego dostępu, ponieważ wyciekanie URL może umożliwić złym aktorom wysyłanie fałszywych wiadomości i potencjalne usunięcie danych użytkownika.

Konfiguruj webhook na Roblox

Po uzyskaniu URL serwera strony trzeciej użyj go do skonfigurowania webhooka na panelu Creator Dashboard.upewnij się, że wykonasz następujące ustawienia:

  • Dodaj URL serwera Guilded lub Discord jako URL Webhook .
  • Włącz niestandardowy Sekret .Chociaż tajemnica jest opcjonalna do ukończenia konfiguracji, powinieneś dodać jedną, aby zapobiec osobom złym przed udawaniem się za Roblox i usunięciem twoich danych.Aby uzyskać więcej informacji o wykorzystaniu sekretu, zobacz Zweryfikuj bezpieczeństwo webhooka.
  • Wybierz Prawo do wniosku o usunięcie pod Wyzwalaczami .

Możesz przetestować webhook za pomocą przycisku Odpowiedź testowa , aby sprawdzić, czy otrzymujesz powiadomienie w kanałach #ogólnych serwera od Roblox.Jeśli nie otrzymujesz powiadomienia, spróbuj ponownie lub sprawdź ustawienia serwera, aby rozwiązać problem.

Example notification on Guilded

Konfiguruj robota

Po dodaniu webhooka użyj go do konfiguracji botu za pomocą następujących kroków:

  1. Otwórz listę Wszystkie serwery poprzez kliknięcie jego ikony lub skorzystaj z skrótu:

    • CtrlS na Windowsie.
    • S na Macu.
  2. Wybierz swój serwer do otrzymywania powiadomień o prawie do usuwania.

  3. Rozszerz listę pod domem serwera i wybierz zarządzaj botami .

  4. Serwer automatycznie tworzy prywatny kanał #ogólny jako domyślny kanał.

  5. Kliknij przycisk Stwórz bot i dodaj nazwę botu. Guilded przekieruje cię na stronę konfiguracji botów.

  6. Wybierz sekcję API na stronie konfiguracji bota.

  7. W sekcji Żetony , kliknij przycisk Generuj żeton .

  8. Zapisz i przechowaj wygenerowany token w bezpiecznym miejsce.

Utwórz klucz Open Cloud API

Aby umożliwić dostęp twojemu botowi stron trzecich do przechowywania danych użytkowników, stwórz klucz API Open Cloud, który może uzyskać dostęp do twoich doświadczeń i dodać uprawnienie Usuń wpis do przechowywania danych do usunięcia.Jeśli używasz uporządkowanych magazynów danych do przechowywania dane osobowe, musisz również dodać uprawnienie Napisz uporządkowanych magazynów danych.Po zakończeniu skopiuj i zapisz klucz API w bezpiecznym miejscu, aby używać go w późniejszych krokach.

Zdobądź identyfikatory doświadczeń i miejsc

Aby bot zlokalizował dane PII żądane przez użytkowników do usunięcia, uzyskaj następujące identyfikatory wszystkich doświadczeń, z których zamierzasz korzystać z botem:

  • ID Wszechświata , unikalny identyfikator twojego doświadczenia.
  • ID miejsca startowego Start Place ID , unikalny identyfikator miejsca startu doświadczenia.

Aby uzyskać te identyfikatory:

  1. Najedź kursorem na miniaturę miniatura, kliknij przycisk i wybierz Kopiuj ID wszechświata oraz Kopiuj początkowe miejsce ID , odpowiednio.

Dodaj kod

Po skonfigurowaniu webhooka, bota i klucza API dla przechowywania danych dodaj je do skryptów, które implementują logikę automatyzacji botów.Poniższy przykład używa Python 3:

  1. Zainstaluj biblioteki Python za pomocą następujących poleceń:

    Zainstaluj biblioteki

    pip3 install guilded.py==1.8.0
    pip3 install requests
    pip3 install urllib3==1.26.6
  2. Skopiuj i zapisz następujące skrypty odpowiadające różnym częściom logiki botów w tym samym katalogu:

    bot_config.py

    BOT_TOKEN = ""
    OPEN_CLOUD_API_KEY = ""
    ROBLOX_WEBHOOK_SECRET = ""
    # Słownik ID miejsca startu do
    # (ID wszechświata, lista nazw (magazynów danych, zakresu i klucza wejściowego)) dla
    # Standardowe magazyny danych
    # Dane użytkownika przechowywane w ramach tych wpisów zostaną usunięte
    STANDARD_DATA_STORE_ENTRIES = {
    # Rozpocznij identyfikator miejsca
    111111111: (
    # ID wszechświata
    222222222,
    [
    ("StandardDataStore1", "Scope1", "Key1_{user_id}"),
    ("StandardDataStore1", "Scope1", "Key2_{user_id}"),
    ("StandardDataStore2", "Scope1", "Key3_{user_id}")
    ]
    ),
    33333333: (
    444444444,
    [
    ("StandardDataStore3", "Scope1", "Key1_{user_id}")
    ]
    )
    }
    # Słownik ID miejsca startu do
    # (ID wszechświata, lista nazw (magazynów danych, zakresu i klucza wejściowego)) dla
    # Zamówione sklepy danych
    # Dane użytkownika przechowywane w ramach tych wpisów zostaną usunięte
    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
    # Zapobiega atakowi odtwarzania w ciągu 300-sekundowego okresu
    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
    # Weryfikuje podpis
    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
    # Prawidłowy podpis
    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):
    # Analizuje otrzymane wiadomości dla ID użytkownika i ID gry
    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):
    # Rozdziela i weryfikuje wiadomość
    user_id, start_place_ids = message_parser.parse_message(message)
    if not user_id or not start_place_ids:
    return
    # Usuwa dane użytkownika standardowych magazynów danych
    [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)}")
    # Usuwa dane przechowywane w zamówionych sklepach danych użytkownika
    [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. Na pliku bot_config.py dla głównej konfiguracji botu:

    1. Ustaw BOT_TOKEN na token wygenerowany przez twój bot.
    2. Ustaw OPEN_CLOUD_API_KEY jako klucz API, który stworzyłeś.
    3. Ustaw jako sekret, który ustawiłeś podczas konfigurowania webhooka na pulpicie nawigacyjnym twórcy.
    4. W słownikach STANDARD_DATA_STORE_ENTRIES i ORDERED_DATA_STORE_ENTRIES w celu znalezienia przechowalni danych każdej rekordu do usuwać:
      1. Dodaj skopiowane ID miejsca startu jako klucze.
      2. Dodaj ID wszechświata jako pierwszy element wartości tuple.
      3. Zastąp drugim elementem tuple nazwę, zakres, nazwę klucza wejściowego i powiązany identyfikator użytkownika twoich magazynów danych.Jeśli używasz innego schematu danych, dostosuj go do swojego własnego schematu danych odpowiednio.
  4. Wykonaj następujące polecenie, aby uruchomić bot:

    Uruchom zautomatyzowany bot Guilded

    python3 guilded_bot.py
  5. Bot zaczyna wtedy słuchać i weryfikować webhooki Roblox w celu uzyskania prawa do żądań usunięcia i dzwoni do punktu końcowego chmury otwartej, aby usunąć odpowiednią sklepdanych.

Testuj

Możesz utworzyć i uruchomić wiadomość testową, aby zweryfikować, że twój niestandardowy program może prawidłowo obsługiwać prośby o usunięcie i usuwać dane PII:

  1. Wyślij żądanie HTTP POST do serwera webhook Guilded lub Discord z następującym ciałożądania:

    Przykładowe żądanie

    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. Jeśli masz sekret webhooka:

    1. Wygeneruj Roblox-Signature poprzez zastosowanie szyfrowania HMAC-SHA256 do klucza sekretu webhooka.
    2. Ustaw aktualny czas za pomocą czasu UTC w sekundach jako Timestamp.
  3. Połącz description w następującym formacie:

    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}`.

    Na przykład:

    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

Twój program powinien być w stanie zidentyfikować, że twoja wiadomość pochodzi z oficjalnego źródła Roblox, ponieważ zakodowałeś wiadomość swoim sekretem.Następnie powinien usunąć dane PII związane z twoim prośba.

Przykładowe ciało

{
"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/dashboard/assets/webhooks/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}