def CreateTestEntities(email_addr): """Create some test Datastore data if specified, but only if running locally. Note that this code doesn't (and shouldn't) delete any existing entities. The risk of such code being accidentally triggered in prod is too great, so if local entities need to be deleted, use the local Datastore viewer (e.g. http://127.0.0.1:8000/datastore). Args: email_addr: Email address of the local users for whom test data should be created. Raises: NotRunningLocally: if called anywhere other than a local deployment. """ if not env_utils.RunningLocally(): raise NotRunningLocally # Create a user entity with all available roles. user = user_models.User.GetOrInsert(email_addr=email_addr) user_models.User.SetRoles(email_addr, constants.USER_ROLE.SET_ALL) username = user_map.EmailToUsername(email_addr) # Create associated SantaHosts for the user. santa_hosts = CreateSantaHosts(2, primary_user=username) # For each SantaHost, create some SantaEvents. for santa_host in santa_hosts: for santa_blockable in CreateSantaBlockables(5): parent_key = model_utils.ConcatenateKeys( user.key, santa_host.key, santa_blockable.key) CreateSantaEvent( santa_blockable, executing_user=username, event_type=constants.EVENT_TYPE.BLOCK_BINARY, host_id=santa_host.key.id(), parent=parent_key) # Create associated Bit9Hosts for the user. bit9_hosts = CreateBit9Hosts(2, users=[username]) # For each Bit9Host, create some Bit9Events. for bit9_host in bit9_hosts: for bit9_binary in CreateBit9Binaries(5): parent_key = model_utils.ConcatenateKeys( user.key, bit9_host.key, bit9_binary.key) CreateBit9Event( bit9_binary, executing_user=username, event_type=constants.EVENT_TYPE.BLOCK_BINARY, host_id=bit9_host.key.id(), parent=parent_key)
def testBlockableKey_BadKey(self): vote = test_utils.CreateVote(self.blockable, user_email=self.user.email) # Take out User key section. vote.key = utils.ConcatenateKeys(self.blockable.key, ndb.Key(base.Vote, vote.key.id())) self.assertIsNone(vote.blockable_key)
def testUserGetOwnEventWithContext_Bundle(self): """Getting an event of the requesting user's by id.""" bundle = test_utils.CreateSantaBundle( bundle_binaries=[self.santa_blockable1]) event = test_utils.CreateSantaEvent( self.santa_blockable1, bundle_key=bundle.key, executing_user=self.user_1.nickname, event_type=constants.EVENT_TYPE.ALLOW_UNKNOWN, host_id=self.santa_host1.key.id(), last_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_1.key, self.santa_host1.key, bundle.key)) test_utils.CreateVote(bundle, user_email=self.user_1.email) params = {'withContext': 'true'} with self.LoggedInUser(user=self.user_1): response = self.testapp.get(self.ROUTE % event.key.urlsafe(), params) output = response.json self.assertIn('application/json', response.headers['Content-type']) self.assertIsInstance(output, dict) self.assertEqual(output['event']['id'], event.key.id()) self.assertEqual(output['host']['id'], output['event']['hostId']) bundle_key = ndb.Key(urlsafe=output['event']['bundleKey']) self.assertEqual(output['blockable']['id'], bundle_key.id()) self.assertIsNotNone(output['vote'])
def testUser_GetOwnEvent_SantaBundle(self): bundle = test_utils.CreateSantaBundle( bundle_binaries=[self.santa_blockable1]) event = test_utils.CreateSantaEvent( self.santa_blockable1, bundle_key=bundle.key, executing_user=self.user_1.nickname, event_type=constants.EVENT_TYPE.BLOCK_UNKNOWN, host_id=self.santa_host1.key.id(), last_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_1.key, self.santa_host1.key, bundle.key)) with self.LoggedInUser(user=self.user_1): response = self.testapp.get(self.ROUTE % bundle.key.id()) output = response.json self.assertIn('application/json', response.headers['Content-type']) self.assertIsInstance(output, dict) self.assertEqual(output['id'], event.key.id()) self.assertEqual(output['hostId'], event.host_id)
def testGetUserBlockRate_WasMax(self): test_utils.CreateEvent( self.blockable, last_blocked_dt=datetime.datetime.utcnow(), parent=utils.ConcatenateKeys(self.user1.key, self.host.key)) was_max, _ = self.host.GetUserBlockRate( self.user1, max_events_to_fetch=1) self.assertTrue(was_max)
def testGetUserBlockRate(self): test_utils.CreateEvent( self.blockable, last_blocked_dt=datetime.datetime.utcnow(), parent=utils.ConcatenateKeys(self.user1.key, self.host.key)) was_max, block_rate = self.host.GetUserBlockRate( self.user1, duration_to_fetch=datetime.timedelta(days=7)) self.assertFalse(was_max) self.assertEqual(1. / 5, block_rate)
def get(self, blockable_id): """Gets user's vote for the given blockable.""" logging.info('Vote handler get method called for %s.', blockable_id) ancestor_key = datastore_utils.ConcatenateKeys( ndb.Key(binary_models.Blockable, blockable_id), self.user.key) # pylint: disable=g-explicit-bool-comparison, singleton-comparison vote = vote_models.Vote.query( vote_models.Vote.in_effect == True, ancestor=ancestor_key).get() # pylint: enable=g-explicit-bool-comparison, singleton-comparison self.respond_json(vote)
def testBlockableKey_MultiPartKey(self): vote = test_utils.CreateVote(self.blockable, user_email=self.user.email) # Add another test_blockable key to simulate a length-two blockable key. vote.key = utils.ConcatenateKeys( self.blockable.key, base.Vote.GetKey(self.blockable.key, self.user.key)) self.assertIsNotNone(vote.blockable_key) self.assertEqual(2, len(vote.blockable_key.pairs())) self.assertEqual(self.blockable.key, vote.blockable_key.parent())
def testHostIsAssociatedWithUser_HasEvent(self): user = test_utils.CreateUser() other_user = test_utils.CreateUser() # Create a host not owned by `user`. host = test_utils.CreateSantaHost(primary_user=other_user.nickname) # Create an Event which was generated by `user`. parent_key = utils.ConcatenateKeys(user.key, host.key, self.santa_blockable.key) test_utils.CreateSantaEvent(self.santa_blockable, host_id=host.key.id(), parent=parent_key) self.assertTrue(host.IsAssociatedWithUser(user))
def testAssociated_HasEvent(self): user = test_utils.CreateUser() other_user = test_utils.CreateUser() # Create a host not owned by `user`. host = test_utils.CreateSantaHost(primary_user=other_user.nickname) # Create an Event which was generated by `user`. blockable = test_utils.CreateSantaBlockable() parent_key = datastore_utils.ConcatenateKeys( user.key, host.key, blockable.key) test_utils.CreateSantaEvent( blockable, host_id=host.key.id(), parent=parent_key) self.assertTrue(model_utils.IsSantaHostAssociatedWithUser(host, user))
def setUp(self): app = webapp2.WSGIApplication( [webapp2.Route(r'/<host_id>', handler=hosts.HostEventRateHandler)]) super(HostEventRateHandlerTest, self).setUp(app) self.user = test_utils.CreateUser() self.santa_blockable = test_utils.CreateSantaBlockable() self.santa_event = test_utils.CreateSantaEvent( self.santa_blockable, host_id=self.santa_host_1.key.id(), last_blocked_dt=datetime.datetime.utcnow(), parent=utils.ConcatenateKeys(self.user.key, self.santa_host_1.key, self.santa_blockable.key))
def setUp(self): super(AssociatedHostHandlerTest, self).setUp() self.user = test_utils.CreateUser() self.admin = test_utils.CreateUser(admin=True) self.santa_blockable = test_utils.CreateSantaBlockable() self.santa_event = test_utils.CreateSantaEvent( self.santa_blockable, host_id=self.santa_host_1.key.id(), executing_user=self.user.nickname, parent=utils.ConcatenateKeys(self.user.key, self.santa_host_1.key, self.santa_blockable.key)) self.bit9_host_1.users = [self.user.nickname] self.bit9_host_1.put()
def GetUserBlockRate(self, user, duration_to_fetch=datetime.timedelta(days=60), max_events_to_fetch=1000): """Calculates the block rate for a given user on this host. "Block rate" is defined as the number of _unique_ blockables a user runs on the host every _workday_ (i.e. 5 out of 7 days per week). Args: user: User, The user for whom to calculate the block rate on this host. duration_to_fetch: datetime.timedelta, The span of time over which the block rate should be calculated. max_events_to_fetch: int, The maximum number of events to be counted. The mitigates the risk that a host with thousands of events results in the datastore query timing out. Returns: (bool, float), A 2-tuple of the form (was_max, block_rate). was_max is True when max_events_to_fetch events were found in the provided time frame. block_rate is the block rate for the given user on this host. Raises: InvalidArgumentError: duration_to_fetch is less than 1 day or max_events_to_fetch is less than 1. """ # Duration must be at least 1 day. if duration_to_fetch.days == 0: raise InvalidArgumentError('Duration must be at least 1 day') elif max_events_to_fetch <= 0: raise InvalidArgumentError('Max Events must be at least 1') threshold_dt = datetime.datetime.utcnow() - duration_to_fetch parent_key = model_utils.ConcatenateKeys(user.key, self.key) query = Event.query(Event.last_blocked_dt >= threshold_dt, ancestor=parent_key).order(-Event.last_blocked_dt) num_events = query.count(limit=max_events_to_fetch) was_max = num_events == max_events_to_fetch # 5 workdays out of 7 days of the week. ratio_of_workdays = 5. / 7 workdays_to_fetch = ratio_of_workdays * duration_to_fetch.days block_rate = float(num_events) / workdays_to_fetch return (was_max, block_rate)
def testAssociatedUserGet(self): """Normal user associated with a host gets it by ID.""" blockable = test_utils.CreateBlockable() with self.LoggedInUser() as user: test_utils.CreateSantaEvent(blockable, host_id=self.santa_host_1.key.id(), executing_user=user.nickname, parent=utils.ConcatenateKeys( user.key, self.santa_host_1.key, blockable.key)) self.assertTrue(self.santa_host_1.IsAssociatedWithUser(user)) response = self.testapp.get('/' + self.santa_host_1.key.id()) output = response.json self.assertIn('application/json', response.headers['Content-type']) self.assertIsInstance(output, dict)
def setUp(self): super(HostExceptionHandlerTest, self).setUp() self.user = test_utils.CreateUser(admin=True) self.santa_blockable = test_utils.CreateSantaBlockable() self.santa_event = test_utils.CreateSantaEvent( self.santa_blockable, host_id=self.santa_host_3.key.id(), executing_user=self.user.nickname, parent=utils.ConcatenateKeys(self.user.key, self.santa_host_3.key, self.santa_blockable.key)) self.santa_blockable.put() self.santa_event.put() self.PatchEnv(settings.ProdEnv, ENABLE_BIGQUERY_STREAMING=True)
def setUp(self): super(LockdownHandlerTest, self).setUp() self.user = test_utils.CreateUser() self.santa_blockable = test_utils.CreateSantaBlockable() self.santa_event = test_utils.CreateSantaEvent( self.santa_blockable, host_id=self.santa_host_3.key.id(), parent=utils.ConcatenateKeys(self.user.key, self.santa_host_3.key, self.santa_blockable.key)) self.santa_blockable.put() self.santa_event.put() self.santa_host_3.client_mode = constants.SANTA_CLIENT_MODE.MONITOR self.santa_host_3.put()
def setUp(self): super(Bit9EventTest, self).setUp() self.user = test_utils.CreateUser() self.bit9_host = test_utils.CreateBit9Host() self.bit9_binary = test_utils.CreateBit9Binary() now = test_utils.Now() self.bit9_event = test_utils.CreateBit9Event( self.bit9_binary, host_id=self.bit9_host.key.id(), executing_user=self.user.nickname, first_blocked_dt=now, last_blocked_dt=now, id='1', parent=utils.ConcatenateKeys(self.user.key, self.bit9_host.key, self.bit9_binary.key))
def setUp(self): app = webapp2.WSGIApplication( [webapp2.Route(r'/<host_id>', handler=hosts.HostExceptionHandler)]) super(HostExceptionHandlerTest, self).setUp(app) self.user = test_utils.CreateUser(admin=True) self.santa_blockable = test_utils.CreateSantaBlockable() self.santa_event = test_utils.CreateSantaEvent( self.santa_blockable, host_id=self.santa_host_3.key.id(), executing_user=self.user.nickname, parent=utils.ConcatenateKeys(self.user.key, self.santa_host_3.key, self.santa_blockable.key)) self.santa_blockable.put() self.santa_event.put() self.PatchEnv(settings.ProdEnv, ENABLE_BIGQUERY_STREAMING=True)
def setUp(self): app = webapp2.WSGIApplication( [webapp2.Route(r'/<host_id>', handler=hosts.LockdownHandler)]) super(LockdownHandlerTest, self).setUp(app) self.user = test_utils.CreateUser() self.santa_blockable = test_utils.CreateSantaBlockable() self.santa_event = test_utils.CreateSantaEvent( self.santa_blockable, host_id=self.santa_host_3.key.id(), parent=utils.ConcatenateKeys(self.user.key, self.santa_host_3.key, self.santa_blockable.key)) self.santa_blockable.put() self.santa_event.put() self.santa_host_3.client_mode = constants.SANTA_CLIENT_MODE.MONITOR self.santa_host_3.put()
def testGet_AssociatedUser(self): user = test_utils.CreateUser() host = test_utils.CreateSantaHost(primary_user=user.nickname) test_utils.CreateExemption(host.key.id()) blockable = test_utils.CreateBlockable() test_utils.CreateSantaEvent( blockable, host_id=host.key.id(), executing_user=user.nickname, parent=datastore_utils.ConcatenateKeys( user.key, host.key, blockable.key)) self.assertTrue(model_utils.IsHostAssociatedWithUser(host, user)) with self.LoggedInUser(user=user): response = self.testapp.get(self.ROUTE % host.key.id()) output = response.json self.assertIn('application/json', response.headers['Content-type']) self.assertIsInstance(output, dict) self.assertIn('exemption', output)
def testGetEventKeysToInsert_Superuser(self): bit9_host = test_utils.CreateBit9Host() bit9_binary = test_utils.CreateBit9Binary() now = test_utils.Now() bit9_event = test_utils.CreateBit9Event( bit9_binary, host_id=bit9_host.key.id(), executing_user=constants.LOCAL_ADMIN.WINDOWS, first_blocked_dt=now, last_blocked_dt=now, id='1', parent=datastore_utils.ConcatenateKeys( self.user.key, bit9_host.key, bit9_binary.key)) users = [self.user.nickname] self.assertEquals( [bit9_event.key], model_utils.GetEventKeysToInsert(bit9_event, users, users))
def testSuccess(self): user = test_utils.CreateUser() other_user = test_utils.CreateUser() santa_host_key_1 = test_utils.CreateSantaHost( primary_user=user.nickname).key santa_host_key_2 = test_utils.CreateSantaHost( primary_user=other_user.nickname).key test_utils.CreateSantaHost(primary_user=other_user.nickname) blockable = test_utils.CreateSantaBlockable() parent_key = datastore_utils.ConcatenateKeys( user.key, santa_host_key_2, blockable.key) test_utils.CreateSantaEvent( blockable, host_id=santa_host_key_2.id(), parent=parent_key) expected_host_ids = sorted([santa_host_key_1.id(), santa_host_key_2.id()]) actual_host_ids = sorted(model_utils.GetSantaHostIdsForUser(user)) self.assertListEqual(expected_host_ids, actual_host_ids)
def testGetByUserId_IsAdmin(self): user = test_utils.CreateUser() bit9_host_id = test_utils.CreateBit9Host(users=[user.nickname]).key.id() santa_host_key = test_utils.CreateSantaHost( primary_user=user.nickname).key santa_host_id = santa_host_key.id() blockable = test_utils.CreateSantaBlockable() event_parent_key = datastore_utils.ConcatenateKeys( user.key, santa_host_key, blockable.key) test_utils.CreateSantaEvent( blockable, host_id=santa_host_id, parent=event_parent_key) with self.LoggedInUser(admin=True): response = self.testapp.get(self.USER_ID_ROUTE % user.key.id()) output = response.json self.assertLen(output, 2) actual_ids = set(host['id'] for host in output) self.assertSetEqual(set([santa_host_id, bit9_host_id]), actual_ids)
def testUser_GetOwnEvent_CaseMismatch(self): user = test_utils.CreateUser() host = test_utils.CreateSantaHost() blockable = test_utils.CreateSantaBlockable() event_parent_key = datastore_utils.ConcatenateKeys( user.key, host.key, blockable.key) event = test_utils.CreateSantaEvent(blockable, executing_user=user.nickname, parent=event_parent_key) with self.LoggedInUser(user=user): response = self.testapp.get(self.ROUTE % blockable.key.id().upper()) output = response.json self.assertIn('application/json', response.headers['Content-type']) self.assertIsInstance(output, dict) self.assertEqual(output['id'], event.key.id()) self.assertEqual(output['hostId'], event.host_id) self.assertIn('Event', output['class_'])
def testGetAssociatedHostIds(self): """User is primary_user of a Host and has an Event on that Host.""" user = test_utils.CreateUser() other_user = test_utils.CreateUser() host = test_utils.CreateSantaHost(primary_user=user.nickname) host_not_primary1 = (test_utils.CreateSantaHost( primary_user=other_user.nickname)) host_not_primary2 = (test_utils.CreateSantaHost( primary_user=other_user.nickname)) blockable = test_utils.CreateSantaBlockable() parent_key = utils.ConcatenateKeys(user.key, host_not_primary1.key, blockable.key) test_utils.CreateSantaEvent(blockable, host_id=host_not_primary1.key.id(), parent=parent_key) associated_ids = santa.SantaHost.GetAssociatedHostIds(user) self.assertIn(host.key.id(), associated_ids) self.assertIn(host_not_primary1.key.id(), associated_ids) self.assertNotIn(host_not_primary2.key.id(), associated_ids)
def setUp(self): app = webapp2.WSGIApplication([ webapp2.Route(r'/', handler=hosts.AssociatedHostHandler, handler_method='GetSelf'), webapp2.Route(r'/<user_id:.*>', handler=hosts.AssociatedHostHandler, handler_method='GetByUserId') ]) super(AssociatedHostHandlerTest, self).setUp(app) self.user = test_utils.CreateUser() self.admin = test_utils.CreateUser(admin=True) self.santa_blockable = test_utils.CreateSantaBlockable() self.santa_event = test_utils.CreateSantaEvent( self.santa_blockable, host_id=self.santa_host_1.key.id(), executing_user=self.user.nickname, parent=utils.ConcatenateKeys(self.user.key, self.santa_host_1.key, self.santa_blockable.key)) self.bit9_host_1.users = [self.user.nickname] self.bit9_host_1.put()
def setUp(self): app = webapp2.WSGIApplication(routes=[events.ROUTES]) super(EventsTest, self).setUp(wsgi_app=app) self.santa_cert = test_utils.CreateSantaCertificate() self.santa_blockable1 = test_utils.CreateSantaBlockable( id='aaaabbbbccccddddeeeeffffgggg', file_name='Product.app', cert_key=self.santa_cert.key, cert_sha256=self.santa_cert.key.id()) self.santa_blockable2 = test_utils.CreateSantaBlockable( id='hhhhiiiijjjjkkkkllllmmmmnnnn', file_name='Another Product.app') self.bit9_blockable1 = test_utils.CreateBit9Binary( id='zzzzaaaayyyybbbbxxxxccccwwww', file_name='notepad++.exe') self.user_1 = test_utils.CreateUser() self.user_2 = test_utils.CreateUser() self.santa_host1 = test_utils.CreateSantaHost( id='AAAAAAAA-1111-BBBB-2222-CCCCCCCCCCCC', recorded_dt=datetime.datetime(2015, 2, 1, 1, 0, 0)) self.santa_host2 = test_utils.CreateSantaHost( id='DDDDDDDD-3333-EEEE-33333-FFFFFFFFFFFF', recorded_dt=datetime.datetime(2015, 2, 1, 1, 0, 0)) self.bit9_host1 = test_utils.CreateSantaHost( id='CHANGE-ME', recorded_dt=datetime.datetime(2015, 2, 1, 1, 0, 0)) self.bit9_host2 = test_utils.CreateSantaHost( id='CHANGE-ME2', recorded_dt=datetime.datetime(2015, 2, 1, 1, 0, 0)) self.santa_event1_from_user1 = test_utils.CreateSantaEvent( self.santa_blockable1, cert_key=self.santa_cert.key, cert_sha256=self.santa_blockable1.cert_sha256, executing_user=self.user_1.nickname, event_type=constants.EVENT_TYPE.ALLOW_UNKNOWN, file_name=self.santa_blockable1.file_name, file_path='/Applications/Product.app/Contents/MacOs', host_id=self.santa_host1.key.id(), last_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_1.key, self.santa_host1.key, self.santa_blockable1.key)) self.santa_event2_from_user1 = test_utils.CreateSantaEvent( self.santa_blockable1, cert_key=self.santa_cert.key, cert_sha256=self.santa_blockable1.cert_sha256, executing_user=self.user_1.nickname, event_type=constants.EVENT_TYPE.ALLOW_UNKNOWN, file_name=self.santa_blockable1.file_name, file_path='/Applications/Product.app/Contents/MacOs', host_id=self.santa_host2.key.id(), last_blocked_dt=datetime.datetime(2015, 4, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 4, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_1.key, self.santa_host2.key, self.santa_blockable1.key)) self.santa_event3_from_user1 = test_utils.CreateSantaEvent( self.santa_blockable2, event_type=constants.EVENT_TYPE.ALLOW_UNKNOWN, executing_user=self.user_1.nickname, file_name=self.santa_blockable2.file_name, file_path='/Applications/Another Product.app/Contents/MacOs', host_id=self.santa_host1.key.id(), last_blocked_dt=datetime.datetime(2015, 5, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 5, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_1.key, self.santa_host1.key, self.santa_blockable2.key)) self.santa_event1_from_user2 = test_utils.CreateSantaEvent( self.santa_blockable1, event_type=constants.EVENT_TYPE.ALLOW_UNKNOWN, executing_user=self.user_2.nickname, file_name=self.santa_blockable1.file_name, file_path='/Applications/Product.app/Contents/MacOs', host_id=self.santa_host2.key.id(), last_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 3, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_2.key, self.santa_host2.key, self.santa_blockable1.key)) self.bit9_event1_from_user1 = test_utils.CreateBit9Event( self.bit9_blockable1, executing_user=self.user_1.nickname, file_name=self.bit9_blockable1.file_name, file_path=r'c:\program files (x86)\notepad++', host_id=self.bit9_host1.key.id(), last_blocked_dt=datetime.datetime(2015, 6, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 6, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_1.key, self.bit9_host1.key, self.bit9_blockable1.key)) self.bit9_event1_from_user2 = test_utils.CreateBit9Event( self.bit9_blockable1, executing_user=self.user_2.nickname, file_name='notepad++.exe', file_path=r'c:\program files (x86)\notepad++', host_id=self.bit9_host2.key.id(), last_blocked_dt=datetime.datetime(2015, 4, 1, 1, 0, 0), first_blocked_dt=datetime.datetime(2015, 4, 1, 1, 0, 0), parent=utils.ConcatenateKeys(self.user_2.key, self.bit9_host2.key, self.bit9_blockable1.key)) self.PatchValidateXSRFToken()
def GetKey(cls, blockable_key, user_key, in_effect=True): # In the in_effect == False case, the None ID field of the key will cause # NDB to generate a random one when the vote is put. vote_id = _IN_EFFECT_KEY_NAME if in_effect else None return datastore_utils.ConcatenateKeys(blockable_key, user_key, ndb.Key(Vote, vote_id))