Skip to content
This repository has been archived by the owner on Jun 12, 2018. It is now read-only.

Commit

Permalink
check for optout authorization and correct params
Browse files Browse the repository at this point in the history
  • Loading branch information
smn committed Nov 11, 2013
1 parent 6ac4657 commit 1a67699
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 13 deletions.
79 changes: 68 additions & 11 deletions go/apps/jsbox/optout.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,59 @@
from vumi.application.sandbox import SandboxResource


class OptoutException(Exception):
pass


def optout_authorized(func):
@inlineCallbacks
def wrapper(self, api, command):
if not (yield self.is_allowed(api)):
returnValue(self.reply(
command, success=False,
reason='Account not allowed to manage optouts.'))

resp = yield func(self, api, command)
returnValue(resp)
return wrapper


def ensure_params(*keys):
def decorator(func):
def wrapper(self, api, command):
for key in keys:
if key not in command:
return self.reply(command, success=False,
reason='Missing key: %s' % (key,))

value = command[key]
# value is not allowed to be `False`, `None` or an empty string.
if not value:
return self.reply(
command, success=False,
reason='Invalid value "%s" for "%s"' % (value, key))

return func(self, api, command)
return wrapper
return decorator


class OptoutResource(SandboxResource):

def get_user_api(self, api):
return self.app_worker.user_api_for_api(api)

def optout_store_for_api(self, api):
return self.app_worker.user_api_for_api(api).optout_store
return self.get_user_api(api).optout_store

def is_allowed(self, api):
user_api = self.get_user_api(api)
d = user_api.get_user_account()
d.addCallback(lambda account: account.can_manage_optouts)
return d

@ensure_params('address_type', 'address_value')
@optout_authorized
@inlineCallbacks
def handle_status(self, api, command):
"""
Expand All @@ -22,17 +70,22 @@ def handle_status(self, api, command):
Returns ``None`` if it doesn't exist.
"""
address_type = command['address_type']
address_value = command['address_value']
optout = yield self.optout_store_for_api(api).get_opt_out(
address_type, address_value)
if optout is not None:
returnValue(self.reply(command, success=True, opted_out=True,
created_at=optout.created_at,
message_id=optout.message))
else:
returnValue(self.reply(command, success=True, opted_out=False))

try:
address_type = command['address_type']
address_value = command['address_value']
optout = yield self.optout_store_for_api(api).get_opt_out(
address_type, address_value)
if optout is not None:
returnValue(self.reply(command, success=True, opted_out=True,
created_at=optout.created_at,
message_id=optout.message))
else:
returnValue(self.reply(command, success=True, opted_out=False))
except (OptoutException,), e:
returnValue(self.reply(command, success=False, reason=e.strerror))

@optout_authorized
def handle_count(self, api, command):
"""
Return a count of however many opt-outs there are
Expand All @@ -43,6 +96,8 @@ def handle_count(self, api, command):
lambda count: self.reply(command, success=True, count=count))
return d

@ensure_params('address_type', 'address_value', 'message_id')
@optout_authorized
@inlineCallbacks
def handle_optout(self, api, command):
"""
Expand All @@ -59,6 +114,8 @@ def handle_optout(self, api, command):
created_at=optout.created_at,
message_id=optout.message))

@ensure_params('address_type', 'address_value')
@optout_authorized
def handle_cancel_optout(self, api, command):
"""
Cancel an opt-out, effectively opting an address_type & address_value
Expand Down
30 changes: 28 additions & 2 deletions go/apps/jsbox/tests/test_optout.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from mock import Mock

from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.defer import inlineCallbacks, returnValue, succeed

from vumi.application.tests.test_sandbox import (
ResourceTestCaseBase, DummyAppWorker)

from go.apps.jsbox.optout import OptoutResource
from go.apps.jsbox.optout import OptoutResource, OptoutException
from go.vumitools.tests.utils import GoPersistenceMixin
from go.vumitools.account import AccountStore
from go.vumitools.contact import ContactStore
Expand Down Expand Up @@ -41,6 +41,7 @@ def setUp(self):
self.manager = self.get_riak_manager()
self.account_store = AccountStore(self.manager)
self.account = yield self.mk_user(self, u'user')
self.account.can_manage_optouts = True
self.contact_store = ContactStore.from_user_account(self.account)
self.optout_store = OptOutStore.from_user_account(self.account)
yield self.contact_store.contacts.enable_search()
Expand All @@ -50,6 +51,8 @@ def setUp(self):
self.user_api = self.app_worker.user_api
self.user_api.contact_store = self.contact_store
self.user_api.optout_store = self.optout_store
self.user_api.get_user_account = Mock(
return_value=succeed(self.account))

self.contact1 = yield self.new_contact(
name=u'A',
Expand Down Expand Up @@ -99,6 +102,20 @@ def test_handle_status_optedin(self):
self.assertTrue(reply['success'])
self.assertFalse(reply['opted_out'])

@inlineCallbacks
def test_ensure_params_missing_key(self):
reply = yield self.dispatch_command('status')
self.assertFalse(reply['success'])
self.assertEqual(reply['reason'],
'Missing key: address_type')

@inlineCallbacks
def test_ensure_params_invalid_value(self):
reply = yield self.dispatch_command('status', address_type=None)
self.assertFalse(reply['success'])
self.assertEqual(reply['reason'],
'Invalid value "None" for "address_type"')

@inlineCallbacks
def test_handle_count(self):
def assert_count(count):
Expand Down Expand Up @@ -131,3 +148,12 @@ def test_handle_cancel_optout(self):
address_value=self.contact1.msisdn)
self.assertTrue(reply['success'])
self.assertFalse(reply['opted_out'])

@inlineCallbacks
def test_account_can_manage_optouts(self):
self.account.can_manage_optouts = False
reply = yield self.dispatch_command('count')
self.assertFalse(reply['success'])
self.assertEqual(
reply['reason'],
'Account not allowed to manage optouts.')
1 change: 1 addition & 0 deletions go/vumitools/account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class UserAccount(Model):
event_handler_config = Json(default=list)
msisdn = Unicode(max_length=255, null=True)
confirm_start_conversation = Boolean(default=False)
can_manage_optouts = Boolean(default=False)
email_summary = Unicode(max_length=255, null=True)
tags = Json(default=[])
routing_table = RoutingTableField(default=RoutingTable({}))
Expand Down

0 comments on commit 1a67699

Please sign in to comment.