Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GDPR script #649

Merged
merged 1 commit into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions anitya/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,31 @@ def get_id(self):
"""
return six.text_type(self.id)

def to_dict(self):
"""
Creates json compatible dict from `User`.

Returns:
dict: `User` object transformed to dictionary.
"""
social_auth = []
for soc_auth in self.social_auth.all():
social_auth.append(
dict(
provider=soc_auth.provider,
extra_data=soc_auth.extra_data,
uid=soc_auth.uid,
)
)

return dict(
id=str(self.id),
email=self.email,
username=self.username,
active=self.active,
social_auth=social_auth,
)


def _api_token_generator(charset=string.ascii_letters + string.digits, length=40):
"""
Expand Down
73 changes: 73 additions & 0 deletions anitya/sar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright 2018 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2, or (at your option) any later
# version. This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY expressed or implied, including the
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details. You
# should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Any Red Hat trademarks that are incorporated in the source
# code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission
# of Red Hat, Inc.
#
"""
This script is used for GDPR SAR (General Data Protection Regulation
Subject Access Requests).
It returns information about specific user saved by Anitya.
It reads SAR_USERNAME and SAR_EMAIL environment variables as keys for
getting data from database.

Authors:
Michal Konecny <[email protected]>
"""

import logging
import os
import json
import sys

from anitya.config import config
from anitya import db

_log = logging.getLogger('anitya')


def main():
'''
Retrieve database entry for user.
'''
db.initialize(config)
_log.setLevel(logging.DEBUG)
sar_username = os.getenv('SAR_USERNAME')
sar_email = os.getenv('SAR_EMAIL')

users = []

if sar_email:
_log.debug('Find users by e-mail {}'.format(sar_email))
users = users + db.User.query.filter_by(email=sar_email).all()

if sar_username:
_log.debug('Find users by username {}'.format(sar_username))
users = users + db.User.query.filter_by(username=sar_username).all()

users_list = []
for user in users:
users_list.append(user.to_dict())

json.dump(users_list, sys.stdout)


if __name__ == '__main__':
_log.info('SAR script start')
main()
_log.info('SAR script end')
31 changes: 31 additions & 0 deletions anitya/tests/db/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,37 @@ def test_is_admin_configured(self):
self.assertTrue(user.is_admin)
self.assertTrue(user.admin)

def test_to_dict(self):
""" Assert the correct dictionary is returned. """
user = models.User(
email='[email protected]',
username='user',
)
user_social_auth = social_models.UserSocialAuth(
user_id=user.id,
user=user,
provider='FAS',
)
user_social_auth.set_extra_data({'wookie': 'too hairy'})
self.session.add(user_social_auth)
self.session.add(user)
self.session.commit()

expected = {
"id": str(user.id),
"email": user.email,
"username": user.username,
"active": user.active,
"social_auth": [{
"provider": user_social_auth.provider,
"extra_data": user_social_auth.extra_data,
"uid": user_social_auth.uid,
}],
}

json = user.to_dict()
self.assertEqual(json, expected)


class ApiTokenTests(DatabaseTestCase):

Expand Down
187 changes: 187 additions & 0 deletions anitya/tests/test_sar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2018 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2, or (at your option) any later
# version. This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY expressed or implied, including the
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details. You
# should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Any Red Hat trademarks that are incorporated in the source
# code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission
# of Red Hat, Inc.
#

'''
anitya tests for GDPR SAR script.
'''

import pytest
import mock
import json

from social_flask_sqlalchemy import models as social_models

import anitya.sar as sar
from anitya.db import models
from anitya.tests.base import DatabaseTestCase


class SARTests(DatabaseTestCase):
""" SAR script tests. """

@pytest.fixture(autouse=True)
def capsys(self, capsys):
""" Use capsys fixture as part of this class. """
self.capsys = capsys

@mock.patch.dict('os.environ', {'SAR_EMAIL': '[email protected]'})
def test_main_email(self):
"""
Assert that correct user data are dumped when providing
e-mail.
"""
user = models.User(
email='[email protected]',
username='user',
active=True,
)
user_social_auth = social_models.UserSocialAuth(
user_id=user.id,
user=user
)

self.session.add(user_social_auth)
self.session.add(user)

user2 = models.User(
email='[email protected]',
username='user2',
active=True,
)
user_social_auth = social_models.UserSocialAuth(
user_id=user2.id,
user=user2
)

self.session.add(user_social_auth)
self.session.add(user2)
self.session.commit()

exp = [{
'id': str(user.id),
'username': user.username,
'email': user.email,
'active': user.active,
'social_auth': [{
'uid': None,
'provider': None,
'extra_data': None
}]
}]

sar.main()

out, err = self.capsys.readouterr()

obs = json.loads(out)

self.assertEqual(exp, obs)

@mock.patch.dict('os.environ', {'SAR_USERNAME': 'user'})
def test_main_username(self):
"""
Assert that correct user data are dumped when providing
username.
"""
user = models.User(
email='[email protected]',
username='user',
active=True,
)
user_social_auth = social_models.UserSocialAuth(
user_id=user.id,
user=user
)

self.session.add(user_social_auth)
self.session.add(user)

user2 = models.User(
email='[email protected]',
username='user2',
active=True,
)
user_social_auth = social_models.UserSocialAuth(
user_id=user2.id,
user=user2
)

self.session.add(user_social_auth)
self.session.add(user2)
self.session.commit()

exp = [{
'id': str(user.id),
'username': user.username,
'email': user.email,
'active': user.active,
'social_auth': [{
'uid': None,
'provider': None,
'extra_data': None
}]
}
]

sar.main()

out, err = self.capsys.readouterr()

obs = json.loads(out)

self.assertEqual(exp, obs)

def test_main_no_env_set(self):
"""
Assert that correct user data are dumped when nothing is provided.
"""
user = models.User(
email='[email protected]',
username='user',
active=True,
)
user_social_auth = social_models.UserSocialAuth(
user_id=user.id,
user=user
)

self.session.add(user_social_auth)
self.session.add(user)

user2 = models.User(
email='[email protected]',
username='user2',
active=True,
)
user_social_auth = social_models.UserSocialAuth(
user_id=user2.id,
user=user2
)

self.session.add(user_social_auth)
self.session.add(user2)
self.session.commit()

sar.main()

out, err = self.capsys.readouterr()

self.assertEqual('[]', out)
1 change: 1 addition & 0 deletions news/PR649.other
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add GDPR SAR script
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def get_requirements(requirements_file='requirements.txt'):
],
packages=find_packages(exclude=['anitya.tests', 'anitya.tests.*']),
include_package_data=True,
scripts=['files/anitya_cron.py'],
scripts=['files/anitya_cron.py', 'anitya/sar.py'],
install_requires=get_requirements(),
test_suite='anitya.tests',
entry_points="""
Expand Down