Die Datenschutz-Grundverordnung (DSGVO) stellt konkrete Anforderungen an die Softwareentwicklung. Als ABAP Cloud Entwickler musst du diese Anforderungen technisch umsetzen. Dieser Artikel zeigt, wie du DSGVO-konforme Anwendungen in ABAP Cloud entwickelst.
DSGVO-Anforderungen für Entwickler
Die DSGVO definiert Rechte für betroffene Personen, die sich direkt auf die Entwicklung auswirken:
| Recht | Artikel | Technische Umsetzung |
|---|---|---|
| Auskunftsrecht | Art. 15 | Datenexport-Funktion |
| Recht auf Berichtigung | Art. 16 | Änderungsfunktionen |
| Recht auf Löschung | Art. 17 | Löschkonzept |
| Recht auf Einschränkung | Art. 18 | Sperrfunktion |
| Datenübertragbarkeit | Art. 20 | Export in Standardformat |
| Widerspruchsrecht | Art. 21 | Consent-Verwaltung |
Technische Prinzipien
Die DSGVO fordert Privacy by Design und Privacy by Default:
Privacy by Design:┌─────────────────────────────────────────────────────┐│ Datenschutz in alle Entwicklungsphasen integrieren │├─────────────────────────────────────────────────────┤│ • Minimale Datenerhebung ││ • Zweckbindung prüfen ││ • Aufbewahrungsfristen definieren ││ • Verschlüsselung implementieren ││ • Zugriffsprotokollierung aktivieren │└─────────────────────────────────────────────────────┘Löschkonzept implementieren (Data Retention)
Ein Löschkonzept definiert, wann und wie personenbezogene Daten gelöscht werden. In ABAP Cloud implementierst du dies über Retention Rules und entsprechende Lösch-Jobs.
Retention-Tabelle definieren
@EndUserText.label: 'Datenkategorie Aufbewahrungsfristen'@AbapCatalog.enhancement.category: #NOT_EXTENSIBLEdefine table zretention_rules { key client : abap.clnt not null; key data_category : abap.char(30) not null; retention_period_days : abap.int4; deletion_method : abap.char(10); -- HARD, SOFT, ANON legal_basis : abap.char(100); last_review_date : abap.dats; responsible_role : abap.char(30);}Löschservice implementieren
CLASS zcl_gdpr_deletion_service DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_deletion_result, data_category TYPE string, records_deleted TYPE i, records_failed TYPE i, deletion_date TYPE timestampl, END OF ty_deletion_result, tt_deletion_results TYPE STANDARD TABLE OF ty_deletion_result WITH EMPTY KEY.
METHODS: execute_retention_deletion RETURNING VALUE(rt_results) TYPE tt_deletion_results,
delete_customer_data IMPORTING iv_customer_id TYPE zcustomer_id iv_deletion_type TYPE string DEFAULT 'HARD' RETURNING VALUE(rv_success) TYPE abap_bool,
get_retention_period IMPORTING iv_data_category TYPE string RETURNING VALUE(rv_days) TYPE i.
PRIVATE SECTION. METHODS: get_deletion_candidates IMPORTING iv_data_category TYPE string iv_cutoff_date TYPE d RETURNING VALUE(rt_keys) TYPE ztt_entity_keys,
perform_hard_delete IMPORTING it_keys TYPE ztt_entity_keys RETURNING VALUE(rv_count) TYPE i,
perform_soft_delete IMPORTING it_keys TYPE ztt_entity_keys RETURNING VALUE(rv_count) TYPE i,
log_deletion IMPORTING iv_category TYPE string iv_count TYPE i iv_method TYPE string.ENDCLASS.
CLASS zcl_gdpr_deletion_service IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. DATA(lt_results) = execute_retention_deletion( ).
LOOP AT lt_results INTO DATA(ls_result). out->write( |Kategorie: { ls_result-data_category }| ). out->write( |Gelöscht: { ls_result-records_deleted }| ). out->write( |Fehlgeschlagen: { ls_result-records_failed }| ). out->write( |---| ). ENDLOOP. ENDMETHOD.
METHOD execute_retention_deletion. " Alle aktiven Retention Rules abrufen SELECT * FROM zretention_rules INTO TABLE @DATA(lt_rules).
DATA(lv_today) = cl_abap_context_info=>get_system_date( ).
LOOP AT lt_rules INTO DATA(ls_rule). " Stichtag berechnen DATA(lv_cutoff_date) = lv_today - ls_rule-retention_period_days.
" Löschkandidaten ermitteln DATA(lt_candidates) = get_deletion_candidates( iv_data_category = ls_rule-data_category iv_cutoff_date = lv_cutoff_date ).
" Löschung durchführen DATA(lv_deleted) = COND #( WHEN ls_rule-deletion_method = 'HARD' THEN perform_hard_delete( lt_candidates ) WHEN ls_rule-deletion_method = 'SOFT' THEN perform_soft_delete( lt_candidates ) WHEN ls_rule-deletion_method = 'ANON' THEN anonymize_records( lt_candidates ) ELSE 0 ).
" Ergebnis protokollieren log_deletion( iv_category = ls_rule-data_category iv_count = lv_deleted iv_method = ls_rule-deletion_method ).
APPEND VALUE #( data_category = ls_rule-data_category records_deleted = lv_deleted records_failed = lines( lt_candidates ) - lv_deleted deletion_date = utclong_current( ) ) TO rt_results. ENDLOOP. ENDMETHOD.
METHOD delete_customer_data. " Einzellöschung für Betroffenenanfrage TRY. CASE iv_deletion_type. WHEN 'HARD'. " Unwiderrufliche Löschung DELETE FROM zcustomer WHERE customer_id = @iv_customer_id. DELETE FROM zcustomer_contact WHERE customer_id = @iv_customer_id. DELETE FROM zcustomer_address WHERE customer_id = @iv_customer_id.
WHEN 'SOFT'. " Markierung als gelöscht UPDATE zcustomer SET is_deleted = @abap_true, deletion_date = @( cl_abap_context_info=>get_system_date( ) ) WHERE customer_id = @iv_customer_id.
WHEN 'ANONYMIZE'. " Anonymisierung statt Löschung UPDATE zcustomer SET first_name = 'ANONYMISIERT', last_name = 'ANONYMISIERT', phone = '000000000', is_anonymized = @abap_true WHERE customer_id = @iv_customer_id. ENDCASE.
rv_success = abap_true.
CATCH cx_sy_open_sql_db. rv_success = abap_false. ENDTRY. ENDMETHOD.
METHOD get_retention_period. SELECT SINGLE retention_period_days FROM zretention_rules WHERE data_category = @iv_data_category INTO @rv_days.
IF sy-subrc <> 0. rv_days = 365 * 10. " Default: 10 Jahre ENDIF. ENDMETHOD.
" ... weitere Methoden
ENDCLASS.RAP Action für Löschung
" Behavior Definitiondefine behavior for ZI_Customer alias Customerimplementation in class zbp_i_customer unique{ // DSGVO-Löschung als Action action deletePersonalData result [1] $self;
// Soft Delete statt physischer Löschung delete ( features : instance );}" Behavior ImplementationMETHOD deletePersonalData. DATA(lo_deletion_service) = NEW zcl_gdpr_deletion_service( ).
LOOP AT keys INTO DATA(ls_key). DATA(lv_success) = lo_deletion_service->delete_customer_data( iv_customer_id = ls_key-CustomerId iv_deletion_type = 'ANONYMIZE' ).
IF lv_success = abap_true. " Ergebnis zurückgeben READ ENTITIES OF zi_customer IN LOCAL MODE ENTITY Customer ALL FIELDS WITH VALUE #( ( %key = ls_key-%key ) ) RESULT DATA(lt_customers).
result = VALUE #( FOR customer IN lt_customers ( %tky = customer-%tky %param = customer ) ). ELSE. APPEND VALUE #( %tky = ls_key-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Löschung fehlgeschlagen' ) ) TO reported-customer. ENDIF. ENDLOOP.ENDMETHOD.Anonymisierung von Daten
Anonymisierung macht Daten unwiderruflich nicht mehr einer Person zuordenbar. Sie ist eine Alternative zur Löschung, wenn die Daten für Statistiken benötigt werden.
Anonymisierungsservice
CLASS zcl_gdpr_anonymization DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_field_rule, field_name TYPE string, anon_method TYPE string, -- MASK, HASH, RANDOM, CONSTANT, NULLIFY constant_value TYPE string, END OF ty_field_rule, tt_field_rules TYPE STANDARD TABLE OF ty_field_rule WITH EMPTY KEY.
METHODS: anonymize_customer IMPORTING iv_customer_id TYPE zcustomer_id RETURNING VALUE(rv_success) TYPE abap_bool,
anonymize_field IMPORTING iv_value TYPE any iv_method TYPE string iv_constant TYPE string OPTIONAL RETURNING VALUE(rv_result) TYPE string,
generate_pseudonym IMPORTING iv_original TYPE string RETURNING VALUE(rv_pseudonym) TYPE string.
ENDCLASS.
CLASS zcl_gdpr_anonymization IMPLEMENTATION.
METHOD anonymize_customer. " Anonymisierungsregeln definieren DATA(lt_rules) = VALUE tt_field_rules( ( field_name = 'FIRST_NAME' anon_method = 'CONSTANT' constant_value = 'ANON' ) ( field_name = 'LAST_NAME' anon_method = 'CONSTANT' constant_value = 'ANON' ) ( field_name = 'EMAIL' anon_method = 'HASH' ) ( field_name = 'PHONE' anon_method = 'MASK' ) ( field_name = 'BIRTH_DATE' anon_method = 'NULLIFY' ) ( field_name = 'ADDRESS' anon_method = 'CONSTANT' constant_value = 'Anonymisiert' ) ).
" Kundendaten laden SELECT SINGLE * FROM zcustomer WHERE customer_id = @iv_customer_id INTO @DATA(ls_customer).
IF sy-subrc <> 0. rv_success = abap_false. RETURN. ENDIF.
" Felder anonymisieren ls_customer-first_name = anonymize_field( iv_value = ls_customer-first_name iv_method = 'CONSTANT' iv_constant = 'ANON' ).
ls_customer-last_name = anonymize_field( iv_value = ls_customer-last_name iv_method = 'CONSTANT' iv_constant = 'ANON' ).
ls_customer-email = anonymize_field( iv_value = ls_customer-email iv_method = 'HASH' ).
ls_customer-phone = anonymize_field( iv_value = ls_customer-phone iv_method = 'MASK' ).
ls_customer-is_anonymized = abap_true. ls_customer-anonymization_date = cl_abap_context_info=>get_system_date( ).
" Anonymisierte Daten speichern UPDATE zcustomer FROM @ls_customer.
rv_success = xsdbool( sy-subrc = 0 ). ENDMETHOD.
METHOD anonymize_field. CASE iv_method. WHEN 'MASK'. " Teilweise maskieren (z.B. ***1234) DATA(lv_length) = strlen( iv_value ). IF lv_length > 4. rv_result = repeat( val = '*' occ = lv_length - 4 ) && substring( val = iv_value off = lv_length - 4 len = 4 ). ELSE. rv_result = repeat( val = '*' occ = lv_length ). ENDIF.
WHEN 'HASH'. " Einweg-Hash (nicht rückrechenbar) TRY. cl_abap_message_digest=>create_md5( EXPORTING if_data = cl_abap_codepage=>convert_to( iv_value ) IMPORTING ef_hashstring = rv_result ). CATCH cx_abap_message_digest. rv_result = 'HASH_ERROR'. ENDTRY.
WHEN 'RANDOM'. " Zufallswert generieren rv_result = cl_system_uuid=>create_uuid_x16_static( ).
WHEN 'CONSTANT'. " Fester Ersatzwert rv_result = iv_constant.
WHEN 'NULLIFY'. " Leerwert rv_result = ''.
WHEN OTHERS. rv_result = iv_value. ENDCASE. ENDMETHOD.
METHOD generate_pseudonym. " Deterministischer Pseudonym (gleiches Original = gleiches Pseudonym) DATA(lv_salt) = 'GDPR_PSEUDONYM_SALT_2024'. DATA(lv_input) = lv_salt && iv_original.
TRY. cl_abap_message_digest=>create_sha256( EXPORTING if_data = cl_abap_codepage=>convert_to( lv_input ) IMPORTING ef_hashstring = DATA(lv_hash) ).
" Kürzeren, lesbaren Pseudonym erstellen rv_pseudonym = 'PSN_' && substring( val = lv_hash len = 12 ). CATCH cx_abap_message_digest. rv_pseudonym = 'PSN_ERROR'. ENDTRY. ENDMETHOD.
ENDCLASS.Pseudonymisierung vs. Anonymisierung
| Aspekt | Pseudonymisierung | Anonymisierung |
|---|---|---|
| Rückführbar | Ja, mit Schlüssel | Nein |
| DSGVO-Status | Personenbezogen | Nicht personenbezogen |
| Anwendung | Verarbeitung | Statistik, Archiv |
| Beispiel | PSN_A3F82B | ANON |
Auskunftsrecht umsetzen (Data Export)
Betroffene haben das Recht, alle über sie gespeicherten Daten in einem maschinenlesbaren Format zu erhalten.
Datenexport-Service
CLASS zcl_gdpr_data_export DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_export_section, category TYPE string, data TYPE REF TO data, END OF ty_export_section, tt_export_sections TYPE STANDARD TABLE OF ty_export_section WITH EMPTY KEY.
METHODS: export_customer_data IMPORTING iv_customer_id TYPE zcustomer_id RETURNING VALUE(rv_json) TYPE string,
export_as_json IMPORTING it_sections TYPE tt_export_sections RETURNING VALUE(rv_json) TYPE string,
create_export_request IMPORTING iv_customer_id TYPE zcustomer_id iv_request_type TYPE string RETURNING VALUE(rv_request_id) TYPE sysuuid_x16.
ENDCLASS.
CLASS zcl_gdpr_data_export IMPLEMENTATION.
METHOD export_customer_data. DATA: lt_sections TYPE tt_export_sections.
" 1. Stammdaten SELECT SINGLE customer_id, first_name, last_name, email, phone, birth_date, created_at FROM zcustomer WHERE customer_id = @iv_customer_id INTO @DATA(ls_customer).
IF sy-subrc = 0. GET REFERENCE OF ls_customer INTO DATA(lr_customer). APPEND VALUE #( category = 'Stammdaten' data = lr_customer ) TO lt_sections. ENDIF.
" 2. Adressen SELECT address_type, street, city, postal_code, country FROM zcustomer_address WHERE customer_id = @iv_customer_id INTO TABLE @DATA(lt_addresses).
IF sy-subrc = 0. GET REFERENCE OF lt_addresses INTO DATA(lr_addresses). APPEND VALUE #( category = 'Adressen' data = lr_addresses ) TO lt_sections. ENDIF.
" 3. Bestellungen SELECT order_id, order_date, status, total_amount, currency FROM zorder WHERE customer_id = @iv_customer_id INTO TABLE @DATA(lt_orders).
IF sy-subrc = 0. GET REFERENCE OF lt_orders INTO DATA(lr_orders). APPEND VALUE #( category = 'Bestellungen' data = lr_orders ) TO lt_sections. ENDIF.
" 4. Kommunikation SELECT communication_date, channel, subject FROM zcommunication_log WHERE customer_id = @iv_customer_id INTO TABLE @DATA(lt_communications).
IF sy-subrc = 0. GET REFERENCE OF lt_communications INTO DATA(lr_comms). APPEND VALUE #( category = 'Kommunikation' data = lr_comms ) TO lt_sections. ENDIF.
" 5. Consent-Historie SELECT consent_type, consent_date, consent_status, ip_address FROM zconsent_log WHERE customer_id = @iv_customer_id INTO TABLE @DATA(lt_consents).
IF sy-subrc = 0. GET REFERENCE OF lt_consents INTO DATA(lr_consents). APPEND VALUE #( category = 'Einwilligungen' data = lr_consents ) TO lt_sections. ENDIF.
" Als JSON exportieren rv_json = export_as_json( lt_sections ). ENDMETHOD.
METHOD export_as_json. DATA: lo_json TYPE REF TO cl_sxml_string_writer.
" JSON-Struktur aufbauen DATA(lo_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
lo_writer->open_element( name = 'gdpr_export' ). lo_writer->open_element( name = 'export_date' ). lo_writer->write_value( |{ cl_abap_context_info=>get_system_date( ) DATE = ISO }| ). lo_writer->close_element( ).
LOOP AT it_sections INTO DATA(ls_section). lo_writer->open_element( name = ls_section-category ).
" Daten zu JSON serialisieren DATA(lv_section_json) = /ui2/cl_json=>serialize( data = ls_section-data->* pretty_name = /ui2/cl_json=>pretty_mode-low_case ).
lo_writer->write_value( lv_section_json ). lo_writer->close_element( ). ENDLOOP.
lo_writer->close_element( ).
rv_json = cl_abap_codepage=>convert_from( lo_writer->get_output( ) ). ENDMETHOD.
METHOD create_export_request. " Export-Anfrage protokollieren rv_request_id = cl_system_uuid=>create_uuid_x16_static( ).
INSERT INTO zgdpr_requests VALUES @( VALUE #( request_id = rv_request_id customer_id = iv_customer_id request_type = iv_request_type " EXPORT, DELETE, RECTIFY request_date = cl_abap_context_info=>get_system_date( ) request_time = cl_abap_context_info=>get_system_time( ) status = 'NEW' requestor = cl_abap_context_info=>get_user_technical_name( ) ) ). ENDMETHOD.
ENDCLASS.RAP Action für Datenexport
" Behavior Definitiondefine behavior for ZC_Customer alias Customer{ // Datenexport als Action mit JSON-Rückgabe action ( features : instance ) exportMyData result [1] $self;}" In der Projection View einen virtuellen Feld für Export-Linkdefine view entity ZC_Customer as projection on ZI_Customer{ key CustomerId, FirstName, LastName, Email,
// Virtuelles Feld für Export-Download @UI.hidden: true cast( '' as abap.string ) as ExportData}Consent Management
Einwilligungen müssen dokumentiert, widerrufbar und nachweisbar sein.
Consent-Datenmodell
@EndUserText.label: 'Consent Kategorien'define table zconsent_types { key client : abap.clnt not null; key consent_type : abap.char(30) not null; description : abap.char(200); legal_basis : abap.char(100); is_mandatory : abap_boolean; default_duration : abap.int4; -- Tage}
@EndUserText.label: 'Consent Log'define table zconsent_log { key client : abap.clnt not null; key consent_id : sysuuid_x16 not null; customer_id : zcustomer_id; consent_type : abap.char(30); consent_status : abap.char(10); -- GRANTED, WITHDRAWN, EXPIRED consent_date : abap.dats; consent_time : abap.tims; expiry_date : abap.dats; ip_address : abap.char(45); user_agent : abap.char(500); consent_text : abap.string; withdrawal_date : abap.dats; withdrawal_reason : abap.char(200);}Consent-Service
CLASS zcl_consent_manager DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. CONSTANTS: BEGIN OF gc_consent_status, granted TYPE string VALUE 'GRANTED', withdrawn TYPE string VALUE 'WITHDRAWN', expired TYPE string VALUE 'EXPIRED', END OF gc_consent_status.
METHODS: grant_consent IMPORTING iv_customer_id TYPE zcustomer_id iv_consent_type TYPE string iv_ip_address TYPE string OPTIONAL iv_user_agent TYPE string OPTIONAL RETURNING VALUE(rv_consent_id) TYPE sysuuid_x16,
withdraw_consent IMPORTING iv_customer_id TYPE zcustomer_id iv_consent_type TYPE string iv_reason TYPE string OPTIONAL RETURNING VALUE(rv_success) TYPE abap_bool,
check_consent IMPORTING iv_customer_id TYPE zcustomer_id iv_consent_type TYPE string RETURNING VALUE(rv_granted) TYPE abap_bool,
get_consent_status IMPORTING iv_customer_id TYPE zcustomer_id RETURNING VALUE(rt_consents) TYPE ztt_consent_status,
expire_old_consents.
ENDCLASS.
CLASS zcl_consent_manager IMPLEMENTATION.
METHOD grant_consent. " Consent-Typ-Details laden SELECT SINGLE * FROM zconsent_types WHERE consent_type = @iv_consent_type INTO @DATA(ls_type).
IF sy-subrc <> 0. RETURN. ENDIF.
" Ablaufdatum berechnen DATA(lv_today) = cl_abap_context_info=>get_system_date( ). DATA(lv_expiry) = COND #( WHEN ls_type-default_duration > 0 THEN lv_today + ls_type-default_duration ELSE '99991231' ).
" Neuen Consent anlegen rv_consent_id = cl_system_uuid=>create_uuid_x16_static( ).
INSERT INTO zconsent_log VALUES @( VALUE #( consent_id = rv_consent_id customer_id = iv_customer_id consent_type = iv_consent_type consent_status = gc_consent_status-granted consent_date = lv_today consent_time = cl_abap_context_info=>get_system_time( ) expiry_date = lv_expiry ip_address = iv_ip_address user_agent = iv_user_agent consent_text = ls_type-description ) ).
" Alten Consent invalidieren UPDATE zconsent_log SET consent_status = @gc_consent_status-withdrawn WHERE customer_id = @iv_customer_id AND consent_type = @iv_consent_type AND consent_id <> @rv_consent_id AND consent_status = @gc_consent_status-granted. ENDMETHOD.
METHOD withdraw_consent. DATA(lv_today) = cl_abap_context_info=>get_system_date( ).
UPDATE zconsent_log SET consent_status = @gc_consent_status-withdrawn, withdrawal_date = @lv_today, withdrawal_reason = @iv_reason WHERE customer_id = @iv_customer_id AND consent_type = @iv_consent_type AND consent_status = @gc_consent_status-granted.
rv_success = xsdbool( sy-subrc = 0 ). ENDMETHOD.
METHOD check_consent. DATA(lv_today) = cl_abap_context_info=>get_system_date( ).
SELECT SINGLE @abap_true FROM zconsent_log WHERE customer_id = @iv_customer_id AND consent_type = @iv_consent_type AND consent_status = @gc_consent_status-granted AND expiry_date >= @lv_today INTO @rv_granted. ENDMETHOD.
METHOD get_consent_status. SELECT consent_type, consent_status, consent_date, expiry_date FROM zconsent_log WHERE customer_id = @iv_customer_id ORDER BY consent_date DESCENDING INTO TABLE @rt_consents. ENDMETHOD.
METHOD expire_old_consents. DATA(lv_today) = cl_abap_context_info=>get_system_date( ).
UPDATE zconsent_log SET consent_status = @gc_consent_status-expired WHERE expiry_date < @lv_today AND consent_status = @gc_consent_status-granted. ENDMETHOD.
ENDCLASS.Consent-Check in der Geschäftslogik
METHOD send_marketing_email. DATA(lo_consent) = NEW zcl_consent_manager( ).
" Prüfen ob Marketing-Einwilligung vorliegt IF lo_consent->check_consent( iv_customer_id = iv_customer_id iv_consent_type = 'MARKETING_EMAIL' ) = abap_false. " Keine Einwilligung - E-Mail nicht senden RAISE EXCEPTION TYPE zcx_no_consent EXPORTING textid = zcx_no_consent=>no_marketing_consent. ENDIF.
" Marketing-E-Mail senden " ...ENDMETHOD.SAP Information Lifecycle Management Integration
SAP ILM bietet eine integrierte Lösung für Datenaufbewahrung und Löschung.
ILM-Konfiguration
CLASS zcl_ilm_integration DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. METHODS: register_business_object IMPORTING iv_object_name TYPE string iv_table_name TYPE tabname,
set_retention_rule IMPORTING iv_object_name TYPE string iv_retention_days TYPE i,
execute_blocking IMPORTING iv_object_name TYPE string,
execute_destruction IMPORTING iv_object_name TYPE string.
ENDCLASS.
CLASS zcl_ilm_integration IMPLEMENTATION.
METHOD register_business_object. " ILM Objekt registrieren über SARA/ILM_DESTRUCTION " Hinweis: In ABAP Cloud über APIs " ... ENDMETHOD.
METHOD set_retention_rule. " Aufbewahrungsregel in ILM setzen " Wird typischerweise über Customizing gemacht " ... ENDMETHOD.
METHOD execute_blocking. " Residence/Retention-Phase " Daten werden für Änderungen gesperrt " ... ENDMETHOD.
METHOD execute_destruction. " End of Purpose - Daten löschen " Wird über ILM Job ausgeführt " ... ENDMETHOD.
ENDCLASS.ILM-fähige CDS View
@EndUserText.label: 'Kundenstamm mit ILM'@ObjectModel.dataCategory: #MASTER@ObjectModel.lifecycle.dateField: 'CreatedAt' -- Für ILM-Bestimmungdefine view entity ZI_Customer_ILM as select from zcustomer{ key customer_id as CustomerId, first_name as FirstName, last_name as LastName,
@Semantics.systemDateTime.createdAt: true created_at as CreatedAt,
@ObjectModel.lifecycle.status: #BLOCKED is_blocked as IsBlocked,
@ObjectModel.lifecycle.status: #DELETED is_deleted as IsDeleted}Checkliste für DSGVO-Compliance
Entwicklungs-Checkliste
| Prüfpunkt | Umgesetzt | Bemerkung |
|---|---|---|
| Datenminimierung | ||
| Nur notwendige Felder erfassen | ☐ | |
| Pflichtfelder auf Minimum reduziert | ☐ | |
| Keine Erfassung “auf Vorrat” | ☐ | |
| Zweckbindung | ||
| Verwendungszweck dokumentiert | ☐ | |
| Consent für jeden Zweck eingeholt | ☐ | |
| Keine Zweckentfremdung | ☐ | |
| Speicherbegrenzung | ||
| Aufbewahrungsfristen definiert | ☐ | |
| Löschjobs implementiert | ☐ | |
| Archivierung konfiguriert | ☐ | |
| Betroffenenrechte | ||
| Auskunftsfunktion vorhanden | ☐ | |
| Berichtigungsfunktion vorhanden | ☐ | |
| Löschfunktion vorhanden | ☐ | |
| Sperrfunktion vorhanden | ☐ | |
| Export-Funktion vorhanden | ☐ | |
| Sicherheit | ||
| Zugriffskontrolle implementiert | ☐ | |
| Verschlüsselung bei Übertragung | ☐ | |
| Protokollierung aktiviert | ☐ | |
| Dokumentation | ||
| Verarbeitungsverzeichnis aktuell | ☐ | |
| Technische Dokumentation vorhanden | ☐ | |
| Datenschutzfolgenabschätzung durchgeführt | ☐ |
Code-Review-Checkliste für DSGVO
" ❌ NICHT DSGVO-konformDATA: gv_customer_ssn TYPE string. " Sensible Daten global
" ✅ DSGVO-konformDATA(lo_customer) = zcl_customer_handler=>get_instance( iv_customer_id ).DATA(lv_masked_ssn) = lo_customer->get_masked_ssn( ).
" ❌ NICHT DSGVO-konform - Logging mit personenbezogenen DatenMESSAGE |Kunde { ls_customer-name } hat Bestellung { lv_order_id }| TYPE 'I'.
" ✅ DSGVO-konform - Keine personenbezogenen Daten im LogMESSAGE |Bestellung { lv_order_id } erstellt| TYPE 'I'.
" ❌ NICHT DSGVO-konform - Unbegrenzter ExportSELECT * FROM zcustomer INTO TABLE @DATA(lt_all_customers).
" ✅ DSGVO-konform - Berechtigungsprüfung und ProtokollierungIF lo_auth->check_export_authorization( ) = abap_true. lo_audit->log_data_export( iv_reason = 'CUSTOMER_REQUEST' ). SELECT * FROM zcustomer WHERE customer_id = @iv_customer_id INTO TABLE @DATA(lt_customer_data).ENDIF.Automatisierte DSGVO-Prüfungen mit ATC
" Custom ATC-Check für DSGVO-Compliance" Prüft auf:" - Hardcodierte personenbezogene Daten" - Fehlende Berechtigungsprüfungen" - Ungeschützte Exporte" - Fehlende ProtokollierungBest Practices
1. Privacy by Design
" Datenminimierung bereits im Datenmodelldefine table zcustomer_minimal { key customer_id : sysuuid_x16; -- Pseudonym statt Klarname display_name : abap.char(100); -- Optional email_hash : abap.char(64); -- Gehashte E-Mail created_at : timestampl; " KEINE: Geburtsdatum, Adresse, Telefon - wenn nicht benötigt}2. Standardmäßiger Datenschutz
" Default: Minimale DatenverarbeitungMETHOD constructor. " Consent wird NICHT automatisch angenommen me->consent_status = gc_consent_status-not_given.
" Marketing ist NICHT standardmäßig aktiviert me->marketing_enabled = abap_false.
" Daten werden NICHT unbegrenzt gespeichert me->retention_date = cl_abap_context_info=>get_system_date( ) + 365.ENDMETHOD.3. Verschlüsselung
" Sensible Daten verschlüsselt speichernMETHOD encrypt_pii_data. TRY. DATA(lo_crypto) = cl_abap_symmetric_cipher=>get_aes256( ). rv_encrypted = lo_crypto->encrypt( if_data = cl_abap_codepage=>convert_to( iv_plain_text ) if_key = get_encryption_key( ) ). CATCH cx_abap_symmetric_cipher. " Fehlerbehandlung ENDTRY.ENDMETHOD.Zusammenfassung
| DSGVO-Anforderung | ABAP Cloud Lösung |
|---|---|
| Löschrecht | RAP Action + Deletion Service |
| Anonymisierung | Anonymization Service |
| Auskunftsrecht | Data Export als JSON |
| Consent Management | Consent Service + CDS Views |
| Aufbewahrung | ILM Integration |
| Protokollierung | Application Logging |
DSGVO-Compliance erfordert eine durchgängige Berücksichtigung von Datenschutzanforderungen in der gesamten Anwendung – vom Datenmodell über die Geschäftslogik bis zur Benutzeroberfläche.
Verwandte Themen
- Authorization Checks - Berechtigungsprüfungen implementieren
- Änderungsbelege - Datenänderungen protokollieren
- RAP Actions & Functions - Aktionen für DSGVO-Operationen