Developpement conforme au RGPD dans ABAP Cloud

Catégorie
Security
Publié
Auteur
Johannes

Le Reglement General sur la Protection des Donnees (RGPD) pose des exigences concretes pour le developpement logiciel. En tant que developpeur ABAP Cloud, vous devez mettre en oeuvre ces exigences techniquement. Cet article montre comment developper des applications conformes au RGPD dans ABAP Cloud.

Exigences RGPD pour les developpeurs

Le RGPD definit des droits pour les personnes concernees qui ont un impact direct sur le developpement :

DroitArticleImplementation technique
Droit d’accesArt. 15Fonction d’export de donnees
Droit de rectificationArt. 16Fonctions de modification
Droit a l’effacementArt. 17Concept de suppression
Droit a la limitationArt. 18Fonction de blocage
Portabilite des donneesArt. 20Export en format standard
Droit d’oppositionArt. 21Gestion du consentement

Principes techniques

Le RGPD exige la Privacy by Design et la Privacy by Default :

Privacy by Design :
┌─────────────────────────────────────────────────────┐
│ Integrer la protection des donnees dans toutes │
│ les phases de developpement │
├─────────────────────────────────────────────────────┤
│ • Collecte minimale de donnees │
│ • Verifier la finalite │
│ • Definir les durees de conservation │
│ • Implementer le chiffrement │
│ • Activer la journalisation des acces │
└─────────────────────────────────────────────────────┘

Implementer un concept de suppression (Data Retention)

Un concept de suppression definit quand et comment les donnees personnelles sont supprimees. Dans ABAP Cloud, vous l’implementez via des Retention Rules et des jobs de suppression correspondants.

Definir une table de retention

@EndUserText.label: 'Durees de conservation par categorie de donnees"
@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE
define 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);
}

Implementer le service de suppression

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( |Categorie : { ls_result-data_category }| ).
out->write( |Supprimes : { ls_result-records_deleted }| ).
out->write( |Echoues : { ls_result-records_failed }| ).
out->write( |---| ).
ENDLOOP.
ENDMETHOD.
METHOD execute_retention_deletion.
" Recuperer toutes les Retention Rules actives
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).
" Calculer la date limite
DATA(lv_cutoff_date) = lv_today - ls_rule-retention_period_days.
" Determiner les candidats a la suppression
DATA(lt_candidates) = get_deletion_candidates(
iv_data_category = ls_rule-data_category
iv_cutoff_date = lv_cutoff_date ).
" Effectuer la suppression
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 ).
" Journaliser le resultat
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.
" Suppression individuelle pour demande de personne concernee
TRY.
CASE iv_deletion_type.
WHEN 'HARD'.
" Suppression irrevocable
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'.
" Marquage comme supprime
UPDATE zcustomer
SET is_deleted = @abap_true,
deletion_date = @( cl_abap_context_info=>get_system_date( ) )
WHERE customer_id = @iv_customer_id.
WHEN 'ANONYMIZE'.
" Anonymisation au lieu de suppression
UPDATE zcustomer
SET first_name = 'ANONYMISE',
last_name = 'ANONYMISE',
email = '[email protected]',
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. " Defaut : 10 ans
ENDIF.
ENDMETHOD.
" ... autres methodes
ENDCLASS.

RAP Action pour la suppression

" Behavior Definition
define behavior for ZI_Customer alias Customer
implementation in class zbp_i_customer unique
{
// Suppression RGPD comme Action
action deletePersonalData result [1] $self;
// Soft Delete au lieu de suppression physique
delete ( features : instance );
}
" Behavior Implementation
METHOD 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.
" Retourner le resultat
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 = 'Suppression echouee' )
) TO reported-customer.
ENDIF.
ENDLOOP.
ENDMETHOD.

Anonymisation des donnees

L’anonymisation rend les donnees irrevocablement non attribuables a une personne. C’est une alternative a la suppression lorsque les donnees sont necessaires pour des statistiques.

Service d’anonymisation

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.
" Definir les regles d'anonymisation
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 = 'Anonymise' )
).
" Charger les donnees client
SELECT SINGLE *
FROM zcustomer
WHERE customer_id = @iv_customer_id
INTO @DATA(ls_customer).
IF sy-subrc <> 0.
rv_success = abap_false.
RETURN.
ENDIF.
" Anonymiser les champs
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( ).
" Sauvegarder les donnees anonymisees
UPDATE zcustomer FROM @ls_customer.
rv_success = xsdbool( sy-subrc = 0 ).
ENDMETHOD.
METHOD anonymize_field.
CASE iv_method.
WHEN 'MASK'.
" Masquer partiellement (par ex. ***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'.
" Hash a sens unique (non reversible)
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'.
" Generer une valeur aleatoire
rv_result = cl_system_uuid=>create_uuid_x16_static( ).
WHEN 'CONSTANT'.
" Valeur de remplacement fixe
rv_result = iv_constant.
WHEN 'NULLIFY'.
" Valeur vide
rv_result = ''.
WHEN OTHERS.
rv_result = iv_value.
ENDCASE.
ENDMETHOD.
METHOD generate_pseudonym.
" Pseudonyme deterministe (meme original = meme pseudonyme)
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) ).
" Creer un pseudonyme plus court et lisible
rv_pseudonym = 'PSN_' && substring( val = lv_hash len = 12 ).
CATCH cx_abap_message_digest.
rv_pseudonym = 'PSN_ERROR'.
ENDTRY.
ENDMETHOD.
ENDCLASS.

Pseudonymisation vs. Anonymisation

AspectPseudonymisationAnonymisation
ReversibleOui, avec une cleNon
Statut RGPDDonnees personnellesDonnees non personnelles
ApplicationTraitementStatistiques, archivage
ExemplePSN_A3F82BANON

Mettre en oeuvre le droit d’acces (Data Export)

Les personnes concernees ont le droit d’obtenir toutes les donnees stockees a leur sujet dans un format lisible par machine.

Service d’export de donnees

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. Donnees de base
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 = 'Donnees de base' data = lr_customer ) TO lt_sections.
ENDIF.
" 2. Adresses
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 = 'Adresses' data = lr_addresses ) TO lt_sections.
ENDIF.
" 3. Commandes
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 = 'Commandes' data = lr_orders ) TO lt_sections.
ENDIF.
" 4. Communications
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 = 'Communications' data = lr_comms ) TO lt_sections.
ENDIF.
" 5. Historique des consentements
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 = 'Consentements' data = lr_consents ) TO lt_sections.
ENDIF.
" Exporter en JSON
rv_json = export_as_json( lt_sections ).
ENDMETHOD.
METHOD export_as_json.
DATA: lo_json TYPE REF TO cl_sxml_string_writer.
" Construire la structure JSON
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 ).
" Serialiser les donnees en JSON
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.
" Journaliser la demande d'export
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 pour l’export de donnees

" Behavior Definition
define behavior for ZC_Customer alias Customer
{
// Export de donnees comme Action avec retour JSON
action ( features : instance ) exportMyData
result [1] $self;
}
" Dans la Projection View, un champ virtuel pour le lien d'export
define view entity ZC_Customer as projection on ZI_Customer
{
key CustomerId,
FirstName,
LastName,
Email,
// Champ virtuel pour le telechargement d'export
@UI.hidden: true
cast( '' as abap.string ) as ExportData
}

Gestion du consentement

Les consentements doivent etre documentes, revocables et prouvables.

Modele de donnees pour le consentement

@EndUserText.label: 'Categories de consentement"
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; -- Jours
}
@EndUserText.label: 'Journal des consentements"
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);
}

Service de consentement

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.
" Charger les details du type de consentement
SELECT SINGLE * FROM zconsent_types
WHERE consent_type = @iv_consent_type
INTO @DATA(ls_type).
IF sy-subrc <> 0.
RETURN.
ENDIF.
" Calculer la date d'expiration
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' ).
" Creer un nouveau consentement
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
) ).
" Invalider l'ancien consentement
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.

Verification du consentement dans la logique metier

METHOD send_marketing_email.
DATA(lo_consent) = NEW zcl_consent_manager( ).
" Verifier si le consentement marketing existe
IF lo_consent->check_consent(
iv_customer_id = iv_customer_id
iv_consent_type = 'MARKETING_EMAIL' ) = abap_false.
" Pas de consentement - ne pas envoyer l'email
RAISE EXCEPTION TYPE zcx_no_consent
EXPORTING
textid = zcx_no_consent=>no_marketing_consent.
ENDIF.
" Envoyer l'email marketing
" ...
ENDMETHOD.

Integration SAP Information Lifecycle Management

SAP ILM offre une solution integree pour la conservation et la suppression des donnees.

Configuration ILM

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.
" Enregistrer l'objet ILM via SARA/ILM_DESTRUCTION
" Note : Dans ABAP Cloud via APIs
" ...
ENDMETHOD.
METHOD set_retention_rule.
" Definir la regle de conservation dans ILM
" Generalement fait via le Customizing
" ...
ENDMETHOD.
METHOD execute_blocking.
" Phase Residence/Retention
" Les donnees sont bloquees pour modification
" ...
ENDMETHOD.
METHOD execute_destruction.
" End of Purpose - Supprimer les donnees
" Execute via le job ILM
" ...
ENDMETHOD.
ENDCLASS.

CDS View compatible ILM

@EndUserText.label: 'Donnees client avec ILM"
@ObjectModel.dataCategory: #MASTER
@ObjectModel.lifecycle.dateField: 'CreatedAt' -- Pour la determination ILM
define 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
}

Checklist pour la conformite RGPD

Checklist de developpement

Point de verificationImplementeRemarque
Minimisation des donnees
Collecter uniquement les champs necessaires
Reduire les champs obligatoires au minimum
Pas de collecte “en reserve”
Limitation de la finalite
Finalite d’utilisation documentee
Consentement obtenu pour chaque finalite
Pas de detournement de finalite
Limitation de la conservation
Durees de conservation definies
Jobs de suppression implementes
Archivage configure
Droits des personnes concernees
Fonction d’information presente
Fonction de rectification presente
Fonction de suppression presente
Fonction de blocage presente
Fonction d’export presente
Securite
Controle d’acces implemente
Chiffrement lors de la transmission
Journalisation activee
Documentation
Registre des traitements a jour
Documentation technique presente
Analyse d’impact effectuee

Checklist de revue de code pour le RGPD

" ❌ NON conforme au RGPD
DATA: gv_customer_ssn TYPE string. " Donnees sensibles globales
" ✅ Conforme au RGPD
DATA(lo_customer) = zcl_customer_handler=>get_instance( iv_customer_id ).
DATA(lv_masked_ssn) = lo_customer->get_masked_ssn( ).
" ❌ NON conforme au RGPD - Logging avec donnees personnelles
MESSAGE |Client { ls_customer-name } a la commande { lv_order_id }| TYPE 'I'.
" ✅ Conforme au RGPD - Pas de donnees personnelles dans le log
MESSAGE |Commande { lv_order_id } creee| TYPE 'I'.
" ❌ NON conforme au RGPD - Export illimite
SELECT * FROM zcustomer INTO TABLE @DATA(lt_all_customers).
" ✅ Conforme au RGPD - Verification d'autorisation et journalisation
IF 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.

Bonnes pratiques

1. Privacy by Design

" Minimisation des donnees des le modele de donnees
define table zcustomer_minimal {
key customer_id : sysuuid_x16; -- Pseudonyme au lieu du nom
display_name : abap.char(100); -- Optionnel
email_hash : abap.char(64); -- Email hashe
created_at : timestampl;
" PAS DE : Date de naissance, adresse, telephone - si non necessaire
}

2. Protection des donnees par defaut

" Par defaut : Traitement minimal des donnees
METHOD constructor.
" Le consentement n'est PAS automatiquement suppose
me->consent_status = gc_consent_status-not_given.
" Le marketing n'est PAS active par defaut
me->marketing_enabled = abap_false.
" Les donnees ne sont PAS conservees indefiniment
me->retention_date = cl_abap_context_info=>get_system_date( ) + 365.
ENDMETHOD.

3. Chiffrement

" Stocker les donnees sensibles chiffrees
METHOD 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.
" Gestion d'erreur
ENDTRY.
ENDMETHOD.

Resume

Exigence RGPDSolution ABAP Cloud
Droit a l’effacementRAP Action + Deletion Service
AnonymisationAnonymization Service
Droit d’accesExport de donnees en JSON
Gestion du consentementConsent Service + CDS Views
ConservationIntegration ILM
JournalisationApplication Logging

La conformite RGPD necessite une prise en compte coherente des exigences de protection des donnees dans l’ensemble de l’application - du modele de donnees a la logique metier jusqu’a l’interface utilisateur.

Sujets connexes