def post(self): self._require_login("/account/register") self._require_registration("/account/register") # Check to make sure that they aren't trying to edit another user current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get("account_id") if target_account_id == current_user_account_id: url = self.request.get("url") secret_key = self.request.get("secret") query = MobileClient.query( MobileClient.messaging_id == url, ancestor=ndb.Key(Account, current_user_account_id) ) if query.count() == 0: # Webhook doesn't exist, add it verification_key = NotificationHelper.verify_webhook(url, secret_key) client = MobileClient( parent=self.user_bundle.account.key, user_id=current_user_account_id, messaging_id=url, display_name=self.request.get("name"), secret=secret_key, client_type=ClientType.WEBHOOK, verified=False, verification_code=verification_key, ) client.put() else: # Webhook already exists. Update the secret current = query.fetch()[0] current.secret = secret_key current.put() self.redirect("/account") else: self.redirect("/")
def test_clients_type(self): clients = [MobileClient( parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='messaging_id_{}'.format(client_type), client_type=client_type) for client_type in ClientType.names.keys()] for client in clients: client.put() self.assertEqual([client.messaging_id for client in MobileClient.clients(['user_id'], client_types=[ClientType.OS_ANDROID])], ['messaging_id_0']) self.assertEqual([client.messaging_id for client in MobileClient.clients(['user_id'], client_types=ClientType.FCM_CLIENTS)], ['messaging_id_1', 'messaging_id_3']) self.assertEqual([client.messaging_id for client in MobileClient.clients(['user_id'], client_types=[ClientType.WEBHOOK])], ['messaging_id_2'])
def _send(cls, users, notification): # Send to FCM clients fcm_clients = MobileClient.clients(users, client_types=ClientType.FCM_CLIENTS) if fcm_clients: cls._defer_fcm(fcm_clients, notification) # Send to webhooks webhook_clients = MobileClient.clients( users, client_types=[ClientType.WEBHOOK]) if webhook_clients: cls._defer_webhook(webhook_clients, notification)
def test_fcm_messaging_ids_unsupported_type(self): user_id = 'user_id' for (token, os) in [('a', ClientType.OS_ANDROID), ('b', ClientType.OS_IOS), ('c', ClientType.WEBHOOK), ('d', ClientType.WEB)]: MobileClient( parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=token, client_type=os, device_uuid=token, display_name=token).put() self.assertEqual(MobileClient.fcm_messaging_ids(user_id), ['b', 'd'])
def ping_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to ping client") gcm_id = request.mobile_id # Find a Client for the current user with the passed GCM ID clients = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id)).fetch(1) if len(clients) == 0: # No Client for user with that push token - bailing return BaseResponse(code=404, message="Invalid push token for user") else: client = clients[0] response = NotificationHelper.send_ping(client) # If we got a response from the send_ping method, it was sent via TBANS # We'll bubble up any errors we got back if response: if response.code == 200: return BaseResponse(code=200, message="Ping sent") else: return BaseResponse( code=response.code, message="Error pinging client - {}".format( response.message)) else: return BaseResponse(code=200, message="Ping sent")
def get(self): self._require_admin() all_clients = MobileClient.query() android = all_clients.filter(MobileClient.client_type == ClientType.OS_ANDROID).count() ios = all_clients.filter(MobileClient.client_type == ClientType.OS_IOS).count() webhook = all_clients.filter(MobileClient.client_type == ClientType.WEBHOOK).count() var = Sitevar.get_by_id('notifications.enable') if var is None or not var.values_json == "true": push_enabled = False else: push_enabled = True self.template_values.update({ 'mobile_users': all_clients.count(), 'android_users': android, 'ios_users': ios, 'webhooks': webhook, 'broadcast_success': self.request.get('broadcast_success'), 'push_enabled': push_enabled, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_dashboard.html') self.response.out.write(template.render(path, self.template_values))
def register_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to register") gcm_id = request.mobile_id os = ClientType.enums[request.operating_system] name = request.name uuid = request.device_uuid query = MobileClient.query( MobileClient.user_id == user_id, MobileClient.device_uuid == uuid, MobileClient.client_type == os) # trying to figure out an elusive dupe bug logging.info("DEBUGGING") logging.info("User ID: {}".format(user_id)) logging.info("UUID: {}".format(uuid)) logging.info("Count: {}".format(query.count())) if query.count() == 0: # Record doesn't exist yet, so add it MobileClient( parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=gcm_id, client_type=os, device_uuid=uuid, display_name=name).put() return BaseResponse(code=200, message="Registration successful") else: # Record already exists, update it client = query.fetch(1)[0] client.messaging_id = gcm_id client.display_name = name client.put() return BaseResponse(code=304, message="Client already exists")
def post(self): if not self.user_bundle.user: self.response.set_status(401) return user_id = self.user_bundle.user.user_id() fcm_token = self.request.get('fcm_token') uuid = self.request.get('uuid') display_name = self.request.get('display_name') client_type = ClientType.WEB query = MobileClient.query( MobileClient.user_id == user_id, MobileClient.device_uuid == uuid, MobileClient.client_type == client_type) if query.count() == 0: # Record doesn't exist yet, so add it MobileClient( parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=fcm_token, client_type=client_type, device_uuid=uuid, display_name=display_name).put() else: # Record already exists, update it client = query.fetch(1)[0] client.messaging_id = fcm_token client.display_name = display_name client.put()
def ping_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to ping client") user_id = PushHelper.user_email_to_id(current_user.email()) gcm_id = request.mobile_id # Find a Client for the current user with the passed GCM ID clients = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id)).fetch(1) if len(clients) == 0: # No Client for user with that push token - bailing return BaseResponse(code=404, message="Invalid push token for user") else: client = clients[0] response = NotificationHelper.send_ping(client) # If we got a response from the send_ping method, it was sent via TBANS # We'll bubble up any errors we got back if response: if response.code == 200: return BaseResponse(code=200, message="Ping sent") else: return BaseResponse(code=response.code, message="Error pinging client - {}".format(response.message)) else: return BaseResponse(code=200, message="Ping sent")
def post(self, client_id): self._require_login() self._require_registration() # Check to make sure the user isn't trying to impersonate another current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: verification = self.request.get('code') webhook = MobileClient.get_by_id(int(client_id), parent=ndb.Key( Account, current_user_account_id)) if webhook.client_type == ClientType.WEBHOOK and current_user_account_id == webhook.user_id: if verification == webhook.verification_code: logging.info("webhook verified") webhook.verified = True webhook.put() self.redirect('/account?webhook_verification_success=1') return else: # Verification failed # Redirect back to the verification page self.redirect('/webhooks/verify/{}?error=1'.format( webhook.key.id())) return self.redirect('/')
def post(self): self._require_login() self._require_registration() # Check to make sure that they aren't trying to edit another user current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: client_id = self.request.get('client_id') client = MobileClient.get_by_id(int(client_id), parent=ndb.Key( Account, current_user_account_id)) if client is not None: # This makes sure that the client actually exists and that this user owns it if client.client_type == ClientType.WEBHOOK: keys = { client.client_type: [(client.messaging_id, client.secret)] } else: keys = {client.client_type: [client.messaging_id]} notification = PingNotification() notification.send(keys) self.redirect('/account') else: self.redirect('/')
def get(self): self._require_admin() all_clients = MobileClient.query() android = all_clients.filter( MobileClient.client_type == ClientType.OS_ANDROID).count() ios = all_clients.filter( MobileClient.client_type == ClientType.OS_IOS).count() web = all_clients.filter( MobileClient.client_type == ClientType.WEB).count() webhook = all_clients.filter( MobileClient.client_type == ClientType.WEBHOOK).count() push_enabled = NotificationsEnable.notifications_enabled() self.template_values.update({ 'mobile_users': all_clients.count(), 'android_users': android, 'ios_users': ios, 'web_users': web, 'webhooks': webhook, 'broadcast_success': self.request.get('broadcast_success'), 'push_enabled': push_enabled, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_dashboard.html') self.response.out.write(template.render(path, self.template_values))
def post(self): self._require_login() self._require_registration() current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: client_id = self.request.get('client_id') webhook = MobileClient.get_by_id(int(client_id), parent=ndb.Key( Account, current_user_account_id)) if webhook.client_type == ClientType.WEBHOOK and current_user_account_id == webhook.user_id: verification_key = NotificationHelper.verify_webhook( webhook.messaging_id, webhook.secret) webhook.verification_code = verification_key webhook.verified = False webhook.put() self.redirect('/account') return else: logging.warning("Not webhook, or wrong owner") else: logging.warning("Users don't match. " + current_user_account_id + "/" + target_account_id) self.redirect('/')
def get_client_ids_for_users(cls, os_type, user_list): output = [] for user in user_list: client_list = MobileClient.query(MobileClient.user_id == user, MobileClient.client_type == ClientType.enums[os_type]).fetch() for client in client_list: output.append(client.messaging_id) return output
def ping_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to ping client") user_id = PushHelper.user_email_to_id(current_user.email()) gcm_id = request.mobile_id # Find a Client for the current user with the passed GCM ID clients = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id)).fetch(1) if len(clients) == 0: # No Client for user with that push token - bailing return BaseResponse(code=404, message="Invalid push token for user") else: client = clients[0] from helpers.tbans_helper import TBANSHelper success = TBANSHelper.ping(client) if success: return BaseResponse(code=200, message="Ping sent") else: return BaseResponse(code=500, message="Failed to ping client")
def get_all_mobile_clients(cls, client_types=[]): output = [] if client_types == []: return output clients = MobileClient.query(MobileClient.client_type.IN(client_types)) for user in clients: output.append(user.user_id) return output
def get_all_mobile_clients(cls, client_types=[]): output = [] if client_types == []: return output clients = MobileClient.query(MobileClient.client_type.IN(client_types)) for user in clients: output.append(user.user_id) return output
def test_defer_webhook(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='messaging_id', client_type=ClientType.WEBHOOK) client.put() notification = MockNotification() TBANSHelper._defer_webhook([client], notification) # Make sure we'll send to FCM clients tasks = self.taskqueue_stub.get_filtered_tasks( queue_names='push-notifications') self.assertEqual(len(tasks), 1) # Make sure our taskqueue tasks execute what we expect with patch.object(TBANSHelper, '_send_webhook') as mock_send_webhook: deferred.run(tasks[0].payload) mock_send_webhook.assert_called_once_with([client], ANY)
def get_client_ids_for_users(cls, user_list, os_types=None): if not user_list: return defaultdict(list) if os_types is None: os_types = ClientType.names.keys() clients = MobileClient.query(MobileClient.user_id.IN(user_list), MobileClient.client_type.IN(os_types), MobileClient.verified == True).fetch() return cls.get_client_ids_for_clients(clients)
def get_client_ids_for_users(cls, user_list, os_types=None): if os_types is None: os_types = ClientType.names.keys() output = defaultdict(list) clients = MobileClient.query(MobileClient.user_id.IN(user_list), MobileClient.client_type.IN(os_types), MobileClient.verified == True).fetch() for client in clients: if client.client_type == ClientType.WEBHOOK: output[client.client_type].append((client.messaging_id, client.secret)) else: output[client.client_type].append(client.messaging_id) return output
def get(self): self._require_admin() webhooks = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() self.template_values.update({ 'webhooks': webhooks, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_webhooks_dashboard.html') self.response.out.write(template.render(path, self.template_values))
def test_ping_fcm_unsupported(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='token', client_type=-1, device_uuid='uuid', display_name='Phone') with self.assertRaises(Exception, msg='Unsupported FCM client type: -1'): TBANSHelper._ping_client(client)
def test_broadcast_android(self): client_type = ClientType.OS_ANDROID messaging_id = 'token' client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id=messaging_id, client_type=client_type, device_uuid='uuid', display_name='Phone') client.put() from notifications.broadcast import BroadcastNotification with patch.object(BroadcastNotification, 'send') as mock_send: TBANSHelper.broadcast([client_type], 'Broadcast', 'Test broadcast') mock_send.assert_called_once_with({client_type: [messaging_id]}) # Make sure we didn't send to FCM or webhooks tasks = self.taskqueue_stub.GetTasks('push-notifications') self.assertEqual(len(tasks), 0)
def test_send_webhook_filter_webhook_clients_verified(self): clients = [ MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='unverified', client_type=ClientType.WEBHOOK, verified=False), MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='verified', client_type=ClientType.WEBHOOK, verified=True) ] with patch( 'models.notifications.requests.webhook_request.WebhookRequest', autospec=True) as mock_init: exit_code = TBANSHelper._send_webhook(clients, MockNotification()) mock_init.assert_called_once_with(ANY, 'verified', ANY) self.assertEqual(exit_code, 0)
def test_send_fcm_unhandled_error(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='messaging_id', client_type=ClientType.OS_IOS) client.put() # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), ['messaging_id']) batch_response = messaging.BatchResponse( [messaging.SendResponse(None, FirebaseError('code', 'message'))]) with patch.object(FCMRequest, 'send', return_value=batch_response), patch( 'logging.error') as mock_error: exit_code = TBANSHelper._send_fcm([client], MockNotification()) self.assertEqual(exit_code, 0) mock_error.assert_called_once_with( 'Unhandled FCM error for messaging_id - code / message') # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), ['messaging_id']) # Check that we didn't queue for a retry tasks = self.taskqueue_stub.get_filtered_tasks( queue_names='push-notifications') self.assertEqual(len(tasks), 0)
def test_send_fcm_invalid_argument_error(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='messaging_id', client_type=ClientType.OS_IOS) client.put() # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), ['messaging_id']) batch_response = messaging.BatchResponse([ messaging.SendResponse(None, InvalidArgumentError('code', 'message')) ]) with patch.object(FCMRequest, 'send', return_value=batch_response), patch( 'logging.critical') as mock_critical: exit_code = TBANSHelper._send_fcm([client], MockNotification()) self.assertEqual(exit_code, 0) mock_critical.assert_called_once_with( 'Invalid argument when sending to FCM - code') # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), ['messaging_id']) # Make sure we haven't queued for a retry tasks = self.taskqueue_stub.get_filtered_tasks( queue_names='push-notifications') self.assertEqual(len(tasks), 0)
def test_send_fcm_sender_id_mismatch_error(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='messaging_id', client_type=ClientType.OS_IOS) client.put() # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), ['messaging_id']) batch_response = messaging.BatchResponse([ messaging.SendResponse(None, SenderIdMismatchError('code', 'message')) ]) with patch.object(FCMRequest, 'send', return_value=batch_response), \ patch.object(MobileClient, 'delete_for_messaging_id', wraps=MobileClient.delete_for_messaging_id) as mock_delete, \ patch('logging.info') as mock_info: exit_code = TBANSHelper._send_fcm([client], MockNotification()) mock_delete.assert_called_once_with('messaging_id') self.assertEqual(exit_code, 0) mock_info.assert_called_with( 'Deleting mismatched client with ID: messaging_id') # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), []) # Make sure we haven't queued for a retry tasks = self.taskqueue_stub.get_filtered_tasks( queue_names='push-notifications') self.assertEqual(len(tasks), 0)
def post(self): self._require_registration() self._require_request_user_is_bundle_user() # Name and URL must be non-None url = self.request.get('url', None) name = self.request.get('name', None) if not url or not name: return self.redirect('/webhooks/add?error=1') # Secret may be none - but we'll generate a secret for the user secret = self.request.get('secret', None) if not secret: import uuid secret = uuid.uuid4().hex current_user_account_id = self.user_bundle.account.key.id() query = MobileClient.query(MobileClient.messaging_id == url, ancestor=ndb.Key(Account, current_user_account_id)) if query.count() == 0: # Webhook doesn't exist, add it from helpers.tbans_helper import TBANSHelper response = TBANSHelper.verify_webhook(url, secret) client = MobileClient( parent=self.user_bundle.account.key, user_id=current_user_account_id, messaging_id=url, display_name=name, secret=secret, client_type=ClientType.WEBHOOK, verified=False, verification_code=response.verification_key) client.put() else: # Webhook already exists. Update the secret current = query.fetch()[0] current.secret = secret current.put() self.redirect('/account')
def test_send_fcm_unavailable_error(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='messaging_id', client_type=ClientType.OS_IOS) client.put() # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), ['messaging_id']) batch_response = messaging.BatchResponse([ messaging.SendResponse(None, UnavailableError('code', 'message')) ]) with patch.object(FCMRequest, 'send', return_value=batch_response), patch( 'logging.error') as mock_error: exit_code = TBANSHelper._send_fcm([client], MockNotification()) self.assertEqual(exit_code, 0) mock_error.assert_called_once_with( 'FCM unavailable - retrying client...') # Sanity check self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), ['messaging_id']) # Check that we queue'd for a retry tasks = self.taskqueue_stub.get_filtered_tasks( queue_names='push-notifications') self.assertEqual(len(tasks), 1) # Make sure our taskqueue tasks execute what we expect with patch.object(TBANSHelper, '_send_fcm') as mock_send_fcm: deferred.run(tasks[0].payload) mock_send_fcm.assert_called_once_with([client], ANY, 1)
def unregister_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to unregister") gcm_id = request.mobile_id query = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id))\ .fetch(keys_only=True) if len(query) == 0: # Record doesn't exist, so we can't remove it return BaseResponse(code=404, message="User doesn't exist. Can't remove it") else: ndb.delete_multi(query) return BaseResponse(code=200, message="User deleted")
def unregister_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to unregister") userID = PushHelper.user_email_to_id(current_user.email()) gcmId = request.mobile_id query = MobileClient.query(MobileClient.messaging_id == gcmId, MobileClient.user_id == userID).fetch(keys_only=True) if len(query) == 0: # Record doesn't exist, so we can't remove it return BaseResponse(code=404, message="User doesn't exist. Can't remove it") else: ndb.delete_multi(query) return BaseResponse(code=200, message="User deleted")
def unregister_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to unregister") userID = PushHelper.user_email_to_id(current_user.email()) gcmId = request.mobile_id query = MobileClient.query(MobileClient.messaging_id == gcmId, ancestor=ndb.Key(Account, userID)).fetch(keys_only=True) if len(query) == 0: # Record doesn't exist, so we can't remove it return BaseResponse(code=404, message="User doesn't exist. Can't remove it") else: ndb.delete_multi(query) return BaseResponse(code=200, message="User deleted")
def test_ping_webhook(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='https://thebluealliance.com', client_type=ClientType.WEBHOOK, secret='secret', display_name='Webhook') with patch.object(TBANSHelper, '_ping_webhook', return_value=True) as mock_ping_webhook: success = TBANSHelper.ping(client) mock_ping_webhook.assert_called_once_with(client) self.assertTrue(success)
def test_ping_client(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='token', client_type=ClientType.OS_IOS, device_uuid='uuid', display_name='Phone') with patch.object(TBANSHelper, '_ping_client', return_value=True) as mock_ping_client: success = TBANSHelper.ping(client) mock_ping_client.assert_called_once_with(client) self.assertTrue(success)
def post(self): self._require_registration() self._require_request_user_is_bundle_user() # Name and URL must be non-None url = self.request.get('url', None) name = self.request.get('name', None) if not url or not name: return self.redirect('/webhooks/add?error=1') # Always generate secret server-side; previously allowed clients to set the secret import uuid secret = uuid.uuid4().hex current_user_account_id = self.user_bundle.account.key.id() query = MobileClient.query(MobileClient.messaging_id == url, ancestor=ndb.Key(Account, current_user_account_id)) if query.count() == 0: # Webhook doesn't exist, add it from helpers.tbans_helper import TBANSHelper verification_key = TBANSHelper.verify_webhook(url, secret) client = MobileClient( parent=self.user_bundle.account.key, user_id=current_user_account_id, messaging_id=url, display_name=name, secret=secret, client_type=ClientType.WEBHOOK, verified=False, verification_code=verification_key) client.put() else: # Webhook already exists. Update the secret current = query.fetch()[0] current.secret = secret current.put() self.redirect('/account')
def get_client_ids_for_users(cls, user_list, os_types=None): if os_types is None: os_types = ClientType.names.keys() output = defaultdict(list) clients = MobileClient.query(MobileClient.user_id.IN(user_list), MobileClient.client_type.IN(os_types), MobileClient.verified == True).fetch() for client in clients: if client.client_type == ClientType.WEBHOOK: output[client.client_type].append( (client.messaging_id, client.secret)) else: output[client.client_type].append(client.messaging_id) return output
def test_send_webhook_multiple(self): clients = [ MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='{}'.format(i), client_type=ClientType.WEBHOOK) for i in range(3) ] batch_response = messaging.BatchResponse([]) with patch.object(WebhookRequest, 'send', return_value=batch_response) as mock_send: exit_code = TBANSHelper._send_webhook(clients, MockNotification()) self.assertEqual(mock_send.call_count, 3) self.assertEqual(exit_code, 0)
def post(self): self._require_registration() # Check to make sure that they aren't trying to edit another user current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: client_id = self.request.get('client_id') client = MobileClient.get_by_id(int(client_id), parent=ndb.Key(Account, current_user_account_id)) if client is not None: # This makes sure that the client actually exists and that this user owns it NotificationHelper.send_ping(client) return self.redirect('/account?ping_sent=1') self.redirect('/')
def unregister_client(self, request): user_id = get_current_user_id(self.headers) if user_id is None: return BaseResponse(code=401, message="Unauthorized to unregister") gcm_id = request.mobile_id query = MobileClient.query(MobileClient.messaging_id == gcm_id, ancestor=ndb.Key(Account, user_id))\ .fetch(keys_only=True) if len(query) == 0: # Record doesn't exist, so we can't remove it return BaseResponse(code=404, message="User doesn't exist. Can't remove it") else: ndb.delete_multi(query) return BaseResponse(code=200, message="User deleted")
def test_ping_webhook_failure(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='https://thebluealliance.com', client_type=ClientType.WEBHOOK, secret='secret', display_name='Webhook') from models.notifications.requests.webhook_request import WebhookRequest with patch.object(WebhookRequest, 'send', return_value=False) as mock_send: success = TBANSHelper._ping_webhook(client) mock_send.assert_called_once() self.assertFalse(success)
def test_fcm_messaging_ids(self): user_id_one = 'user_id_one' token_one = 'token1' token_two = 'token2' user_id_two = 'user_id_two' token_three = 'token3' user_id_three = 'user_id_three' for (user_id, tokens) in [(user_id_one, [token_one, token_two]), (user_id_two, [token_three])]: for token in tokens: MobileClient( parent=ndb.Key(Account, user_id), user_id=user_id, messaging_id=token, client_type=ClientType.OS_IOS, device_uuid=token[::-1], display_name='Phone').put() self.assertEqual(MobileClient.fcm_messaging_ids(user_id_one), [token_one, token_two]) self.assertEqual(MobileClient.fcm_messaging_ids(user_id_two), [token_three]) self.assertEqual(MobileClient.fcm_messaging_ids(user_id_three), [])
def register_client(self, request): current_user = endpoints.get_current_user() if current_user is None: return BaseResponse(code=401, message="Unauthorized to register") userId = PushHelper.user_email_to_id(current_user.email()) gcmId = request.mobile_id os = ClientType.enums[request.operating_system] if MobileClient.query( MobileClient.messaging_id==gcmId ).count() == 0: # Record doesn't exist yet, so add it MobileClient( messaging_id = gcmId, user_id = userId, client_type = os ).put() return BaseResponse(code=200, message="Registration successful") else: # Record already exists, don't bother updating it again return BaseResponse(code=304, message="Client already exists")
def get(self): webhooks = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() failures = [] for client in webhooks: response = TBANSHelper.ping_webhook(client) if not response.code == 200: failures.append(client.key) count = len(failures) if failures: ndb.delete_multi(failures) logging.info("Deleted {} broken webhooks".format(count)) template_values = {'count': count} path = os.path.join(os.path.dirname(__file__), '../../templates/admin/webhooks_clear_do.html') self.response.out.write(template.render(path, template_values))
def get(self): clients = MobileClient.query().fetch() clients = sorted(clients, key=lambda x: (x.messaging_id, x.updated)) last = None to_remove = [] last = None for client in clients: if last is not None and client.messaging_id == last.messaging_id: logging.info("Removing") to_remove.append(client.key) last = client count = len(to_remove) if to_remove: ndb.delete_multi(to_remove) logging.info("Removed {} duplicate mobile clients".format(count)) template_values = {'count': count} path = os.path.join(os.path.dirname(__file__), '../../templates/admin/mobile_clear_do.html') self.response.out.write(template.render(path, template_values))
def get(self): webhooks = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch() failures = [] notification = PingNotification()._render_webhook() for key in webhooks: if not NotificationSender.send_webhook(notification, [(key.messaging_id, key.secret)]): failures.append(key.key) count = len(failures) if failures: ndb.delete_multi(failures) logging.info("Deleted {} broken webhooks".format(count)) template_values = {'count': count} path = os.path.join(os.path.dirname(__file__), '../../templates/admin/webhooks_clear_do.html') self.response.out.write(template.render(path, template_values))
def post(self): self._require_registration() current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: client_id = self.request.get('client_id') webhook = MobileClient.get_by_id(int(client_id), parent=ndb.Key(Account, current_user_account_id)) if webhook.client_type == ClientType.WEBHOOK and current_user_account_id == webhook.user_id: verification_key = NotificationHelper.verify_webhook(webhook.messaging_id, webhook.secret) webhook.verification_code = verification_key webhook.verified = False webhook.put() self.redirect('/account') return else: logging.warning("Not webhook, or wrong owner") else: logging.warning("Users don't match. "+current_user_account_id+"/"+target_account_id) self.redirect('/')
def post(self): self._require_registration() # Check to make sure that they aren't trying to edit another user current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: client_id = self.request.get('client_id') client = MobileClient.get_by_id(int(client_id), parent=ndb.Key(Account, current_user_account_id)) if client is not None: # This makes sure that the client actually exists and that this user owns it if client.client_type == ClientType.WEBHOOK: keys = {client.client_type: [(client.messaging_id, client.secret)]} else: keys = {client.client_type: [client.messaging_id]} notification = PingNotification() notification.send(keys) self.redirect('/account') else: self.redirect('/')
def post(self, client_id): self._require_registration() # Check to make sure the user isn't trying to impersonate another current_user_account_id = self.user_bundle.account.key.id() target_account_id = self.request.get('account_id') if target_account_id == current_user_account_id: verification = self.request.get('code') webhook = MobileClient.get_by_id(int(client_id), parent=ndb.Key(Account, current_user_account_id)) if webhook.client_type == ClientType.WEBHOOK and current_user_account_id == webhook.user_id: if verification == webhook.verification_code: logging.info("webhook verified") webhook.verified = True webhook.put() self.redirect('/account?webhook_verification_success=1') return else: # Verification failed # Redirect back to the verification page self.redirect('/webhooks/verify/{}?error=1'.format(webhook.key.id())) return self.redirect('/')
def post(self, client_id): self._require_registration() self._require_request_user_is_bundle_user() current_user_account_id = self.user_bundle.account.key.id() if not current_user_account_id: return self.redirect('/') verification = self.request.get('code') if not verification: return self.redirect('/webhooks/verify/{}?error=1'.format(webhook.key.id())) webhook = MobileClient.get_by_id(int(client_id), parent=ndb.Key(Account, current_user_account_id)) if not webhook or webhook.client_type != ClientType.WEBHOOK or current_user_account_id != webhook.user_id: return self.redirect('/') if verification == webhook.verification_code: webhook.verified = True webhook.put() return self.redirect('/account?webhook_verification_success=1') else: # Redirect back to the verification page return self.redirect('/webhooks/verify/{}?error=1'.format(webhook.key.id()))
def post(self): self._require_registration() self._require_request_user_is_bundle_user() current_user_account_id = self.user_bundle.account.key.id() if not current_user_account_id: return self.redirect('/') client_id = self.request.get('client_id') if not client_id: return self.redirect('/') webhook = MobileClient.get_by_id(int(client_id), parent=ndb.Key(Account, current_user_account_id)) if not webhook or webhook.client_type != ClientType.WEBHOOK or current_user_account_id != webhook.user_id: return self.redirect('/') from helpers.tbans_helper import TBANSHelper response = TBANSHelper.verify_webhook(webhook.messaging_id, webhook.secret) webhook.verification_code = response.verification_key webhook.verified = False webhook.put() return self.redirect('/account')
def mobile_clients(self): user_id = self.user.user_id() return MobileClient.query(ancestor=ndb.Key(Account, user_id)).fetch()
def delete_bad_gcm_token(cls, key): logging.info("removing bad GCM token: "+key) to_delete = MobileClient.query(MobileClient.messaging_id == key).fetch(keys_only=True) ndb.delete_multi(to_delete)
def update_token(cls, old, new): logging.info("updating token"+old+"\n->"+new) to_update = MobileClient.query(MobileClient.messaging_id == old).fetch() for model in to_update: model.messaging_id = new model.put()
def update_token(cls, old, new): to_update = MobileClient.query(MobileClient.messaging_id == old).fetch() for model in to_update: model.messaging_id = new model.put()