blob: 015fad49dbee290080c36aa2137e6d70dce90ebd [file] [log] [blame]
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
"""Cron and task handlers for syncing with wipeoute-lite and deleting users."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import json
import logging
import httplib2
from google.appengine.api import app_identity
from businesslogic import work_env
from framework import cloud_tasks_helpers
from framework import framework_constants
from framework import jsonfeed
from framework import urls
from oauth2client.client import GoogleCredentials
WIPEOUT_ENDPOINT = 'https://emporia-pa.googleapis.com/v1/apps/%s'
MAX_BATCH_SIZE = 10000
MAX_DELETE_USERS_SIZE = 1000
def authorize():
credentials = GoogleCredentials.get_application_default()
credentials = credentials.create_scoped(framework_constants.OAUTH_SCOPE)
return credentials.authorize(httplib2.Http(timeout=60))
class WipeoutSyncCron(jsonfeed.FlaskInternalTask):
"""Enqueue tasks for sending user lists to wipeout-lite and deleting deleted
users fetched from wipeout-lite."""
def HandleRequest(self, mr):
batch_param = mr.GetIntParam('batchsize', default_value=MAX_BATCH_SIZE)
# Use batch_param as batch_size unless it is None or 0.
batch_size = min(batch_param, MAX_BATCH_SIZE)
total_users = self.services.user.TotalUsersCount(mr.cnxn)
total_batches = int(total_users / batch_size)
# Add an extra batch to process remainder user emails.
if total_users % batch_size:
total_batches += 1
if not total_batches:
logging.info('No users to report.')
return
for i in range(total_batches):
params = dict(limit=batch_size, offset=i * batch_size)
task = cloud_tasks_helpers.generate_simple_task(
urls.SEND_WIPEOUT_USER_LISTS_TASK + '.do', params)
cloud_tasks_helpers.create_task(
task, queue=framework_constants.QUEUE_SEND_WIPEOUT_USER_LISTS)
task = cloud_tasks_helpers.generate_simple_task(
urls.DELETE_WIPEOUT_USERS_TASK + '.do', {})
cloud_tasks_helpers.create_task(
task, queue=framework_constants.QUEUE_FETCH_WIPEOUT_DELETED_USERS)
def GetWipeoutSyncCron(self, **kwargs):
return self.handler(**kwargs)
class SendWipeoutUserListsTask(jsonfeed.FlaskInternalTask):
"""Sends a batch of monorail users to wipeout-lite."""
def HandleRequest(self, mr):
limit = mr.GetIntParam('limit')
assert limit != None, 'Missing param limit'
offset = mr.GetIntParam('offset')
assert offset != None, 'Missing param offset'
emails = self.services.user.GetAllUserEmailsBatch(
mr.cnxn, limit=limit, offset=offset)
accounts = [{'id': email} for email in emails]
service = authorize()
self.sendUserLists(service, accounts)
def sendUserLists(self, service, accounts):
app_id = app_identity.get_application_id()
endpoint = WIPEOUT_ENDPOINT % app_id
resp, data = service.request(
'%s/verifiedaccounts' % endpoint,
method='POST',
headers={'Content-Type': 'application/json; charset=UTF-8'},
body=json.dumps(accounts))
logging.info(
'Received response, %s with contents, %s', resp, data)
def PostSendWipeoutUserListsTask(self, **kwargs):
return self.handler(**kwargs)
class DeleteWipeoutUsersTask(jsonfeed.FlaskInternalTask):
"""Fetches deleted users from wipeout-lite and enqueues tasks to delete
those users from Monorail's DB."""
def HandleRequest(self, mr):
limit = mr.GetIntParam('limit', MAX_DELETE_USERS_SIZE)
limit = min(limit, MAX_DELETE_USERS_SIZE)
service = authorize()
deleted_user_data = self.fetchDeletedUsers(service)
deleted_emails = [user_object['id'] for user_object in deleted_user_data]
total_batches = int(len(deleted_emails) / limit)
if len(deleted_emails) % limit:
total_batches += 1
for i in range(total_batches):
start = i * limit
end = start + limit
params = dict(emails=','.join(deleted_emails[start:end]))
task = cloud_tasks_helpers.generate_simple_task(
urls.DELETE_USERS_TASK + '.do', params)
cloud_tasks_helpers.create_task(
task, queue=framework_constants.QUEUE_DELETE_USERS)
def fetchDeletedUsers(self, service):
app_id = app_identity.get_application_id()
endpoint = WIPEOUT_ENDPOINT % app_id
resp, data = service.request(
'%s/deletedaccounts' % endpoint,
method='GET',
headers={'Content-Type': 'application/json; charset=UTF-8'})
logging.info(
'Received response, %s with contents, %s', resp, data)
return json.loads(data)
def PostDeleteWipeoutUsersTask(self, **kwargs):
return self.handler(**kwargs)
class DeleteUsersTask(jsonfeed.FlaskInternalTask):
"""Deletes users from Monorail's DB."""
def HandleRequest(self, mr):
"""Delete users with the emails given in the 'emails' param."""
emails = mr.GetListParam('emails', default_value=[])
assert len(emails) <= MAX_DELETE_USERS_SIZE, (
'We cannot delete more than %d users at once, current users: %d' %
(MAX_DELETE_USERS_SIZE, len(emails)))
if len(emails) == 0:
logging.info("No user emails found in deletion request")
return
with work_env.WorkEnv(mr, self.services) as we:
we.ExpungeUsers(emails, check_perms=False)
def PostDeleteUsersTask(self, **kwargs):
return self.handler(**kwargs)