-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Centralize privacy/deletion handler registration and verification.
- Loading branch information
Showing
10 changed files
with
224 additions
and
182 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# SPDX-FileCopyrightText: 2024 Mark Liffiton <[email protected]> | ||
# | ||
# SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
"""Implementation of personal data deletion for CodeHelp.""" | ||
|
||
from gened.data_deletion import register_handler | ||
from gened.db import get_db | ||
|
||
|
||
class CodeHelpDeletionHandler: | ||
"""CodeHelp implementation of personal data deletion.""" | ||
|
||
def delete_user_data(self, user_id: int) -> None: | ||
"""Delete/Anonymize personal data for a user while preserving non-personal data for analysis.""" | ||
db = get_db() | ||
|
||
# Anonymize personal data in queries | ||
db.execute(""" | ||
UPDATE queries | ||
SET code = CASE | ||
WHEN code IS NOT NULL THEN '[deleted]' | ||
ELSE NULL | ||
END, | ||
error = CASE | ||
WHEN error IS NOT NULL THEN '[deleted]' | ||
ELSE NULL | ||
END, | ||
issue = '[deleted]', | ||
context_name = '[deleted]', | ||
context_string_id = NULL, | ||
user_id = -1 | ||
WHERE user_id = ? | ||
""", [user_id]) | ||
|
||
# Anonymize personal data in chats | ||
db.execute(""" | ||
UPDATE chats | ||
SET topic = '[deleted]', | ||
chat_json = '[]', | ||
context_name = '[deleted]', | ||
context_string_id = NULL, | ||
user_id = -1 | ||
WHERE user_id = ? | ||
""", [user_id]) | ||
|
||
db.commit() | ||
|
||
def delete_class_data(self, class_id: int) -> None: | ||
"""Delete/Anonymize personal data for a class while preserving non-personal data for analysis.""" | ||
db = get_db() | ||
|
||
# Remove context names and configs as they may contain personal information | ||
db.execute(""" | ||
UPDATE contexts | ||
SET name = '[deleted]' || id, | ||
config = '{}' | ||
WHERE class_id = ? | ||
""", [class_id]) | ||
|
||
# Remove context strings as they may contain personal information | ||
db.execute(""" | ||
DELETE FROM context_strings | ||
WHERE id IN ( | ||
SELECT context_string_id | ||
FROM queries | ||
WHERE role_id IN ( | ||
SELECT id FROM roles WHERE class_id = ? | ||
) | ||
UNION | ||
SELECT context_string_id | ||
FROM chats | ||
WHERE role_id IN ( | ||
SELECT id FROM roles WHERE class_id = ? | ||
) | ||
) | ||
""", [class_id, class_id]) | ||
|
||
# Anonymize personal data in queries | ||
db.execute(""" | ||
UPDATE queries | ||
SET code = CASE | ||
WHEN code IS NOT NULL THEN '[deleted]' | ||
ELSE NULL | ||
END, | ||
error = CASE | ||
WHEN error IS NOT NULL THEN '[deleted]' | ||
ELSE NULL | ||
END, | ||
issue = '[deleted]', | ||
context_name = '[deleted]', | ||
context_string_id = NULL, | ||
user_id = -1 | ||
WHERE role_id IN ( | ||
SELECT id FROM roles WHERE class_id = ? | ||
) | ||
""", [class_id]) | ||
|
||
# Anonymize personal data in chats | ||
db.execute(""" | ||
UPDATE chats | ||
SET topic = '[deleted]', | ||
chat_json = '[]', | ||
context_name = '[deleted]', | ||
context_string_id = NULL, | ||
user_id = -1 | ||
WHERE role_id IN ( | ||
SELECT id FROM roles WHERE class_id = ? | ||
) | ||
""", [class_id]) | ||
|
||
db.commit() | ||
|
||
|
||
def register_with_gened() -> None: | ||
"""Register CodeHelp deletion handler with the gened framework.""" | ||
register_handler(CodeHelpDeletionHandler()) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# SPDX-FileCopyrightText: 2024 Mark Liffiton <[email protected]> | ||
# | ||
# SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
"""Interface and registry for personal data deletion handlers. | ||
This module defines the interface that Gen-Ed applications must implement | ||
for handling personal data deletion and provides the registration mechanism | ||
for those handlers. | ||
""" | ||
|
||
from typing import Protocol | ||
|
||
|
||
class DeletionHandler(Protocol): | ||
"""Protocol defining the interface for personal data deletion handlers.""" | ||
def delete_user_data(self, user_id: int) -> None: | ||
"""Delete/anonymize all personal data for the given user.""" | ||
... | ||
|
||
def delete_class_data(self, class_id: int) -> None: | ||
"""Delete/anonymize all personal data for the given class.""" | ||
... | ||
|
||
|
||
_handler: DeletionHandler | None = None | ||
|
||
|
||
def register_handler(handler: DeletionHandler) -> None: | ||
"""Register the application's deletion handler implementation.""" | ||
global _handler | ||
_handler = handler | ||
|
||
|
||
def get_handler() -> DeletionHandler | None: | ||
"""Get the registered deletion handler.""" | ||
return _handler | ||
|
||
|
||
def delete_user_data(user_id: int) -> None: | ||
"""Delete/anonymize all personal data for the given user.""" | ||
if _handler is None: | ||
raise RuntimeError("No deletion handler registered") | ||
_handler.delete_user_data(user_id) | ||
|
||
|
||
def delete_class_data(class_id: int) -> None: | ||
"""Delete/anonymize all personal data for the given class.""" | ||
if _handler is None: | ||
raise RuntimeError("No deletion handler registered") | ||
_handler.delete_class_data(class_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.