def setUp(self): app = webapp2.WSGIApplication( [webapp2.Route(r'', handler=FakeHandler)]) super(XsrfTest, self).setUp(app, patch_generate_token=False) self.user_email = user_map.UsernameToEmail('test') self.Login(self.user_email) self.user_id = users.get_current_user().user_id()
def testAdminAddingUser(self): """Admin adding a user through a post request.""" id_ = user_map.UsernameToEmail('user4') pre_post_user = base.User.get_by_id(id_) pre_post_user_existed = (pre_post_user is not None) params = {'roles': constants.USER_ROLE.TRUSTED_USER} with self.LoggedInUser(admin=True): response = self.testapp.post('/' + id_, params) expected_dict = { 'id': id_, 'roles': [constants.USER_ROLE.TRUSTED_USER] } datastore_user = base.User.get_by_id(id_) output = response.json self.assertIn('application/json', response.headers['Content-type']) self.assertIsInstance(output, dict) self.assertDictContainsSubset(expected_dict, output) self.assertDictContainsSubset(expected_dict, datastore_user.to_dict()) self.assertFalse(pre_post_user_existed)
def get(self, blockable_id): # pylint: disable=g-bad-name blockable = base_models.Blockable.get_by_id(blockable_id) if not blockable: self.abort(httplib.NOT_FOUND, explanation='Blockable not found') username = self.request.get('asUser') if username: self.RequireCapability(constants.PERMISSIONS.VIEW_OTHER_EVENTS) user = user_models.User.GetById(user_map.UsernameToEmail(username)) else: user = self.user # If the blockable is a bundle, search by the 'bundle_key' property instead # of 'blockable_key'. blockable_filter = (santa_models.SantaEvent.bundle_key == blockable.key if isinstance(blockable, santa_models.SantaBundle) else base_models.Event.blockable_key == blockable.key) event_query = (base_models.Event.query(ancestor=user.key).filter( blockable_filter).order(-base_models.Event.last_blocked_dt)) event = event_query.get() response_data = event if event: with_context = (self.request.get('withContext').lower() == 'true') response_data = _GetEventContext([event ])[0] if with_context else event self.respond_json(response_data)
def post(self, user_id): """Post handler for users.""" logging.debug('UserHandler POST method called with ID: %s', user_id) email_addr = user_map.UsernameToEmail(user_id) new_roles = self.request.get_all('roles') base_db.User.SetRoles(email_addr, new_roles) user = base_db.User.GetOrInsert(email_addr=email_addr) self.respond_json(user)
def testCopyLocalRules_NoPreviousHosts(self): old_user = test_utils.CreateUser(email=user_map.UsernameToEmail('foo')) new_user = test_utils.CreateUser(email=user_map.UsernameToEmail('bar')) test_utils.CreateBit9Host( id='12345', users=[old_user.nickname], policy_key=ndb.Key(bit9_db.Bit9Policy, '22222')) host = bit9_test_utils.CreateComputer( id=12345, policy_id=22222, users='{0}\\{1},{0}\\{2}'.format( settings.AD_DOMAIN, old_user.nickname, new_user.nickname)) occurred_dt = datetime.datetime.utcnow() sync._PersistBit9Host(host, occurred_dt).wait() self.assertEntityCount(bit9_db.Bit9Rule, 0) self.assertEntityCount(bit9_db.RuleChangeSet, 0) self.assertTaskCount(constants.TASK_QUEUE.BQ_PERSISTENCE, 1) self.DrainTaskQueue(constants.TASK_QUEUE.BQ_PERSISTENCE) self.assertEntityCount(bigquery_db.HostRow, 1)
def testCopyLocalRules_Success(self): old_user = test_utils.CreateUser(email=user_map.UsernameToEmail('foo')) new_user = test_utils.CreateUser(email=user_map.UsernameToEmail('bar')) policy_key = ndb.Key(bit9_db.Bit9Policy, '22222') host1 = test_utils.CreateBit9Host( id='12345', users=[old_user.nickname], policy_key=policy_key) test_utils.CreateBit9Host( id='67890', users=[new_user.nickname], policy_key=policy_key) blockable1 = test_utils.CreateBit9Binary() test_utils.CreateBit9Rule( blockable1.key, host_id=host1.key.id(), user_key=old_user.key) blockable2 = test_utils.CreateBit9Binary() test_utils.CreateBit9Rule( blockable2.key, host_id=host1.key.id(), user_key=old_user.key) host = bit9_test_utils.CreateComputer( id=67890, policy_id=22222, users='{0}\\{1},{0}\\{2}'.format( settings.AD_DOMAIN, old_user.nickname, new_user.nickname)) occurred_dt = datetime.datetime.utcnow() sync._PersistBit9Host(host, occurred_dt).wait() self.assertEntityCount(bit9_db.Bit9Rule, 4) # 2 New + 2 Old self.assertEntityCount(bit9_db.RuleChangeSet, 2) rules_for_host1 = bit9_db.Bit9Rule.query( bit9_db.Bit9Rule.host_id == host1.key.id()).fetch() self.assertEqual(2, len(rules_for_host1)) self.assertSameElements( [blockable1.key, blockable2.key], [rule.key.parent() for rule in rules_for_host1]) self.assertTaskCount(constants.TASK_QUEUE.BQ_PERSISTENCE, 1) self.DrainTaskQueue(constants.TASK_QUEUE.BQ_PERSISTENCE) self.assertEntityCount(bigquery_db.HostRow, 1)
def GetKeysToInsert(self, logged_in_users, host_owners): """Returns the list of keys with which this event should be inserted.""" if settings.EVENT_CREATION == constants.EVENT_CREATION.EXECUTING_USER: if self.run_by_local_admin: usernames = logged_in_users else: usernames = [self.executing_user ] if self.executing_user else [] else: # HOST_OWNERS usernames = host_owners emails = [user_map.UsernameToEmail(username) for username in usernames] keys = [] for email in emails: key_pairs = [(User, email.lower()), (Host, self.host_id)] key_pairs += self.blockable_key.pairs() key_pairs += [(Event, '1')] keys.append(ndb.Key(pairs=key_pairs)) return keys
def testUsernameToEmail(self): self.assertEqual('user@' + settings.USER_EMAIL_DOMAIN, user_map.UsernameToEmail('user'))
def _PersistBit9Host(computer, occurred_dt): """Creates a Bit9Host from the Event protobuf if one does not already exist. NOTE: This function could be transactional but, at least for now, host puts in multiple requests don't really need to be processed in a fixed order. last_event_dt is the only frequently modified property and there's currently no need for it to be perfectly accurate. Args: computer: api.Computer object associated with the event. occurred_dt: datetime object corresponding to the time of the event. Returns: ndb.Future that resolves when the host is updated. """ host_id = str(computer.id) policy = computer.policy_id policy_key = (ndb.Key(bit9.Bit9Policy, str(policy)) if policy is not None else None) hostname = bit9_utils.ExpandHostname( bit9_utils.StripDownLevelDomain(computer.name)) policy_entity = policy_key.get() mode = (policy_entity.enforcement_level if policy_entity is not None else constants.HOST_MODE.UNKNOWN) # Grab the corresponding Bit9Host. bit9_host = yield bit9.Bit9Host.get_by_id_async(host_id) existing_users = set(bit9_host.users if bit9_host is not None else []) extracted_users = list(bit9_utils.ExtractHostUsers(computer.users)) # Ignore any 'Desktop Window Manager' users, otherwise a user can temporarily # become disassociated with their machine. If they vote for something to be # locally whitelisted during such a period, they won't get a rule for it. incoming_users = set() for extracted_user in extracted_users: if r'Window Manager\DWM-' in extracted_user: logging.warning('Ignoring user "%s"', extracted_user) else: incoming_users.add(extracted_user) # If there are incoming users, either because it was only all 'Desktop Window # Manager' entries, or because Bit9 didn't report any users for whatever # reason, then just stick with the existing users, otherwise we'll # disassociate the machine from the user. if not incoming_users: incoming_users = existing_users # Perform initialization for users new to this host. new_users = incoming_users - existing_users for new_user in new_users: # Create User if we haven't seen this user before. email = user_map.UsernameToEmail(new_user) user = user_models.User.GetOrInsert(email_addr=email) # Copy the user's local rules over from a pre-existing host. yield _CopyLocalRules(user.key, host_id) # List of all row action that need to be persisted. row_actions = [] # Doesn't exist? Guess we better fix that. if bit9_host is None: logging.info('Creating new Bit9Host') bit9_host = bit9.Bit9Host(id=host_id, hostname=hostname, last_event_dt=occurred_dt, policy_key=policy_key, users=sorted(list(incoming_users))) row_actions.append(constants.HOST_ACTION.FIRST_SEEN) else: changed = False if not bit9_host.last_event_dt or bit9_host.last_event_dt < occurred_dt: bit9_host.last_event_dt = occurred_dt changed = True if bit9_host.hostname != hostname: logging.info('Hostname for %s changed from %s to %s', host_id, bit9_host.hostname, hostname) bit9_host.hostname = hostname changed = True if bit9_host.policy_key != policy_key: bit9_host.policy_key = policy_key changed = True row_actions.append(constants.HOST_ACTION.MODE_CHANGE) if existing_users != incoming_users: existing_users_list = sorted(list(existing_users)) incoming_users_list = sorted(list(incoming_users)) logging.info('Users for %s changed from %s to %s', host_id, existing_users_list, incoming_users_list) bit9_host.users = incoming_users_list changed = True row_actions.append(constants.HOST_ACTION.USERS_CHANGE) if not changed: raise ndb.Return() logging.info('Attempting to put Bit9Host...') yield bit9_host.put_async() for action in row_actions: tables.HOST.InsertRow(device_id=host_id, timestamp=(bit9_host.recorded_dt if action == constants.HOST_ACTION.FIRST_SEEN else bit9_host.last_event_dt), action=action, hostname=hostname, platform=constants.PLATFORM.WINDOWS, users=sorted(list(incoming_users)), mode=mode)
def RandomEmail(): return user_map.UsernameToEmail('noreply+%s' % RandomLetters(8))
def post(self, uuid): futures = [] # Create an User for the primary_user on any preflight if one doesn't # already exist. primary_user = self.parsed_json.get(santa_const.PREFLIGHT.PRIMARY_USER) user = base_db.User.GetOrInsert( user_map.UsernameToEmail(primary_user)) # Ensures the returned username is consistent with the User entity. primary_user = user.nickname # Create a SantaHost on the first preflight. first_preflight = not self.host if first_preflight: self.host = santa_db.SantaHost(key=self.host_key) self.host.client_mode = settings.SANTA_DEFAULT_CLIENT_MODE futures.append(self._CreateNewLocalRules(uuid, user.key)) # Update host entity on every sync. self.host.serial_num = self.parsed_json.get( santa_const.PREFLIGHT.SERIAL_NUM) self.host.hostname = self.parsed_json.get( santa_const.PREFLIGHT.HOSTNAME) self.host.primary_user = primary_user self.host.santa_version = self.parsed_json.get( santa_const.PREFLIGHT.SANTA_VERSION) self.host.os_version = self.parsed_json.get( santa_const.PREFLIGHT.OS_VERSION) self.host.os_build = self.parsed_json.get( santa_const.PREFLIGHT.OS_BUILD) self.host.last_preflight_dt = datetime.datetime.utcnow() self.host.last_preflight_ip = self.request.remote_addr reported_mode = self.parsed_json.get(santa_const.PREFLIGHT.CLIENT_MODE) if reported_mode != self.host.client_mode: msg = 'Client mode mismatch. Expected: %s. Actual: %s' % ( self.host.client_mode, reported_mode) logging.info(msg) futures.append(base_db.AuditLog.CreateAsync(self.host, msg)) if self.parsed_json.get(santa_const.PREFLIGHT.REQUEST_CLEAN_SYNC): logging.info('Client requested clean sync') self.host.rule_sync_dt = None # Save host entity. futures.append(self.host.put_async()) # If the big red button is pressed, override the self.host.client_mode # set in datastore with either MONITOR or LOCKDOWN for this response only. actual_client_mode = self.host.client_mode big_red_button = big_red.BigRedButton() if big_red_button.stop_stop_stop: actual_client_mode = common_const.SANTA_CLIENT_MODE.MONITOR elif big_red_button.go_go_go: actual_client_mode = common_const.SANTA_CLIENT_MODE.LOCKDOWN # Prepare response. response = { santa_const.PREFLIGHT.BATCH_SIZE: ( settings.SANTA_EVENT_BATCH_SIZE), santa_const.PREFLIGHT.CLIENT_MODE: actual_client_mode, santa_const.PREFLIGHT.WHITELIST_REGEX: ( self.host.directory_whitelist_regex), santa_const.PREFLIGHT.BLACKLIST_REGEX: ( self.host.directory_blacklist_regex), santa_const.PREFLIGHT.CLEAN_SYNC: not self.host.rule_sync_dt, santa_const.PREFLIGHT.BUNDLES_ENABLED: ( settings.SANTA_BUNDLES_ENABLED), } if self.host.should_upload_logs: response[santa_const.PREFLIGHT.UPLOAD_LOGS_URL] = ( blobstore.create_upload_url('/api/santa/logupload/%s' % uuid)) # Verify all futures resolved successfully. for future in futures: future.check_success() # If this is the first preflight, create a FIRST_SEEN HostRow. This has to # occur after the new SantaHost entity is put(), since SantaHost.recorded_dt # is an auto_now_add. if first_preflight: new_host = ndb.Key('Host', uuid).get() bigquery.HostRow.DeferCreate( device_id=uuid, timestamp=new_host.recorded_dt, action=common_const.HOST_ACTION.FIRST_SEEN, hostname=new_host.hostname, platform=common_const.PLATFORM.MACOS, users=santa_db.SantaHost.GetAssociatedUsers(uuid), mode=new_host.client_mode) self.respond_json(response)
# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS-IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for user.py.""" from upvote.gae.datastore import test_utils from upvote.gae.datastore.models import user as user_models from upvote.gae.lib.testing import basetest from upvote.gae.shared.common import settings from upvote.gae.shared.common import user_map from upvote.shared import constants _TEST_EMAIL = user_map.UsernameToEmail('testemail') # Done for the sake of brevity. USER = constants.USER_ROLE.USER TRUSTED_USER = constants.USER_ROLE.TRUSTED_USER ADMINISTRATOR = constants.USER_ROLE.ADMINISTRATOR class UserTest(basetest.UpvoteTestCase): """Test User model.""" def setUp(self): super(UserTest, self).setUp() self._voting_weights = settings.VOTING_WEIGHTS self.PatchEnv(settings.ProdEnv, ENABLE_BIGQUERY_STREAMING=True)
def testAdminGetUnknownUser(self): """Admin attempting to get information on an unknown user.""" with self.LoggedInUser(admin=True): unknown_user = user_map.UsernameToEmail('blahblahblah') self.testapp.get('/' + unknown_user, status=httplib.NOT_FOUND)
def _PersistBit9Host(computer, occurred_dt): """Creates a Bit9Host from the Event protobuf if one does not already exist. NOTE: This function could be transactional but, at least for now, host puts in multiple requests don't really need to be processed in a fixed order. last_event_dt is the only frequently modified property and there's currently no need for it to be perfectly accurate. Args: computer: api.Computer object associated with the event. occurred_dt: datetime object corresponding to the time of the event. Returns: ndb.Future that resolves when the host is updated. """ host_id = str(computer.id) policy = computer.policy_id policy_key = (ndb.Key(bit9.Bit9Policy, str(policy)) if policy is not None else None) hostname = utils.ExpandHostname( rest_utils.StripDownLevelDomain(computer.name)) policy_entity = policy_key.get() mode = (policy_entity.enforcement_level if policy_entity is not None else constants.HOST_MODE.UNKNOWN) # Grab the corresponding Bit9Host. bit9_host = yield bit9.Bit9Host.get_by_id_async(host_id) host_users = list(rest_utils.ExtractHostUsers(computer.users)) # Perform initialization for users new to this host. existing_users = set(bit9_host.users if bit9_host is not None else []) new_host_users = set(host_users) - existing_users for username in new_host_users: # Create User if we haven't seen this user before. email = user_map.UsernameToEmail(username) user = base.User.GetOrInsert(email_addr=email) # Copy the user's local rules over from a pre-existing host. yield _CopyLocalRules(user.key, host_id) # List of all row action that need to be persisted. row_actions = [] # Doesn't exist? Guess we better fix that. if bit9_host is None: logging.info('Creating new Bit9Host') bit9_host = bit9.Bit9Host(id=host_id, hostname=hostname, last_event_dt=occurred_dt, policy_key=policy_key, users=host_users) row_actions.append(constants.HOST_ACTION.FIRST_SEEN) else: changed = False if not bit9_host.last_event_dt or bit9_host.last_event_dt < occurred_dt: bit9_host.last_event_dt = occurred_dt changed = True if bit9_host.hostname != hostname: bit9_host.hostname = hostname changed = True if bit9_host.policy_key != policy_key: bit9_host.policy_key = policy_key changed = True row_actions.append(constants.HOST_ACTION.MODE_CHANGE) if set(bit9_host.users) != set(host_users): bit9_host.users = host_users changed = True row_actions.append(constants.HOST_ACTION.USERS_CHANGE) if not changed: raise ndb.Return() logging.info('Attempting to put Bit9Host...') yield bit9_host.put_async() for action in row_actions: bigquery.HostRow.DeferCreate( device_id=host_id, timestamp=(bit9_host.recorded_dt if action == constants.HOST_ACTION.FIRST_SEEN else bit9_host.last_event_dt), action=action, hostname=hostname, platform=constants.PLATFORM.WINDOWS, users=host_users, mode=mode)
def testGetKeysToInsert(self): keys = self.event_1.GetKeysToInsert(['foo', 'bar'], []) self.assertEqual(1, len(keys)) expected_email = user_map.UsernameToEmail(self.event_1.executing_user) self.assertEqual(expected_email, keys[0].pairs()[0][1])