def get(self): params = collections.defaultdict(dict) search_perms = base_handler.VerifyAllPermissionTypes( permissions.SEARCH) for model_type in search_perms: if not search_perms[model_type]: continue model = model_util.TypeNameToModel(model_type) if not hasattr(model, 'SEARCH_FIELDS'): continue params[model_type]['fields'] = model.SEARCH_FIELDS can_retrieve_own = False retrieve_own_perms = base_handler.VerifyAllPermissionTypes( permissions.RETRIEVE_OWN) for volume_type in retrieve_own_perms: if retrieve_own_perms[volume_type]: params[volume_type][permissions.RETRIEVE_OWN] = True can_retrieve_own = True if can_retrieve_own: params['user'] = base.GetCurrentUser().user.nickname() self.response.out.write(util.ToSafeJson(params))
def post(self, volume_key): """Handles POST requests.""" try: db_key = db.Key(volume_key) except db.BadKeyError as e: logging.warning('Bad volume_key "%s" provided: %s', volume_key, e) return self.error(httplib.NOT_FOUND) self.entity = self.SECRET_MODEL.get(db_key) if not self.entity: return self.error(httplib.NOT_FOUND) if self.entity and not self.entity.active: return self.error(httplib.BAD_REQUEST) self.VerifyXsrfToken(base_settings.CHANGE_OWNER_ACTION) base_handler.VerifyPermissions(permissions.CHANGE_OWNER, base.GetCurrentUser(), self.PERMISSION_TYPE) new_entity = self.entity.Clone() new_entity.owner = self.request.get('new_owner') new_entity.put() self.AUDIT_LOG_MODEL.Log( entity=self.entity, request=self.request, message=('Owner changed from "%s" to "%s"' % (self.entity.owner, new_entity.owner)))
def VerifyPermissions(self, required_permission, user=None, permission_type=None): """Verifies a valid user is logged in. Args: required_permission: permission string from permissions.*. user: optional, base.User entity; default current user. permission_type: optional, string, one of permission.TYPE_* variables. if omitted, self.PERMISSION_TYPE is used. Returns: base.User object of the current user. Raises: base.AccessDeniedError: there was a permissions issue. """ permission_type = permission_type or self.PERMISSION_TYPE if user is None: user = base.GetCurrentUser() base_handler.VerifyPermissions(required_permission, user, permission_type) return user
def CheckRetrieveAuthorizationAndNotifyOwner(self, entity): """Checks whether the user is authorised to retrieve the secret. Args: entity: base.BasePassPhrase instance of retrieved object. Raises: errors.AccessDeniedError: user lacks any retrieval permissions. errors.AccessError: user lacks a specific retrieval permission. """ user = base.GetCurrentUser() try: self.VerifyPermissions(permissions.RETRIEVE, user=user) except errors.AccessDeniedError: try: self.VerifyPermissions(permissions.RETRIEVE_CREATED_BY, user=user) if str(entity.created_by) not in str(user.user.email()): raise except errors.AccessDeniedError: self.VerifyPermissions(permissions.RETRIEVE_OWN, user=user) if user.email not in entity.owners: raise if user.email not in entity.owners: SendRetrievalEmail(self.PERMISSION_TYPE, entity, user)
def get(self): """Handles GET requests.""" log_type = self.request.get('log_type') base_handler.VerifyPermissions( permissions.MASTER, base.GetCurrentUser(), log_type) start = self.request.get('start_next', None) log_model = models_util.TypeNameToLogModel(log_type) logs_query = log_model.all() logs_query.order('-paginate_mtime') if start: logs_query.filter('paginate_mtime <', start) logs = logs_query.fetch(PER_PAGE + 1) more = len(logs) == PER_PAGE + 1 start_next = None if more: start_next = logs[-1].paginate_mtime logs = [db.to_dict(log) for log in logs[:PER_PAGE]] for log in logs: log['user'] = str(log['user']) log['mtime'] = str(log['mtime']) params = { 'logs': logs, 'log_type': log_type, 'more': more, 'start': start, 'start_next': start_next, } self.response.out.write(util.ToSafeJson(params))
def get(self): params = collections.defaultdict(dict) search_perms = handlers.VerifyAllPermissionTypes(permissions.SEARCH) if search_perms[permissions.TYPE_BITLOCKER]: params['bitlocker'][ 'fields'] = models.BitLockerVolume.SEARCH_FIELDS if search_perms[permissions.TYPE_FILEVAULT]: params['filevault'][ 'fields'] = models.FileVaultVolume.SEARCH_FIELDS if search_perms[permissions.TYPE_LUKS]: params['luks']['fields'] = models.LuksVolume.SEARCH_FIELDS if search_perms[permissions.TYPE_PROVISIONING]: provisioning_fields = models.ProvisioningVolume.SEARCH_FIELDS params['provisioning']['fields'] = provisioning_fields can_retrieve_own = False retrieve_own_perms = handlers.VerifyAllPermissionTypes( permissions.RETRIEVE_OWN) for volume_type in retrieve_own_perms: if retrieve_own_perms[volume_type]: params[volume_type][permissions.RETRIEVE_OWN] = True can_retrieve_own = True if can_retrieve_own: params['user'] = base.GetCurrentUser().user.nickname() self.response.out.write(util.ToSafeJson(params))
def get(self, serials): base_handler.VerifyPermissions(permissions.RETRIEVE, base.GetCurrentUser(), permissions.TYPE_APPLE_FIRMWARE) inventory_service = service_factory.GetInventoryService() res = { 'active': [], 'retired': [], } for serial in serials.split(','): if not inventory_service.IsRetiredMac(serial): res['active'].append(serial) continue entity = firmware.AppleFirmwarePassword.GetLatestForTarget(serial) if entity: firmware.AppleFirmwarePasswordAccessLog.Log( message='GET', entity=entity, request=self.request) res['retired'].append({ 'serial': serial, 'password': entity.password }) else: res['retired'].append({'serial': serial, 'password': '******'}) self.response.write(util.ToSafeJson(res))
def VerifyPermissions(self, required_permission, user=None, permission_type=None): """Verifies a valid user is logged in. Args: required_permission: permission string from permissions.*. user: optional, base.User entity; default current user. permission_type: optional, string, one of permission.TYPE_* variables. if omitted, self.PERMISSION_TYPE is used. Returns: base.User object of the current user. Raises: base.AccessDeniedError: there was a permissions issue. """ # TODO(user): Consider making the method accept a list of checks # to be performed, making CheckRetrieveAuthorization simpler. permission_type = permission_type or self.PERMISSION_TYPE if user is None: user = base.GetCurrentUser() base_handler.VerifyPermissions(required_permission, user, permission_type) return user
def XsrfTokenValidate(token, action, user=None, timestamp=None, time_=time): """Generate an XSRF token.""" if not token: return False if not user: # TODO(user): drop the unused user arg, find a way to cache user. user = base.GetCurrentUser().email if not timestamp: try: # Request objects return Unicode encoded tokens. token = token.encode('utf-8') _, timestr = base64.urlsafe_b64decode(token).split( XSRF_DELIMITER, 1) timestamp = float(timestr) except ValueError: logging.exception('ValueError obtaining timestamp from token: %s', token) return False if timestamp + XSRF_VALID_TIME < time_.time(): return False if token != XsrfTokenGenerate(action, user, timestamp): return False return True
def _VerifyEscrowPermission(self): try: base.GetCurrentUser() except base.AccessDeniedError: pass else: return super(DellFirmwarePassword, self)._VerifyEscrowPermission() raise base.AccessDeniedError
def RetrieveSecret(self, target_id): """Handles a GET request to retrieve a secret.""" self.VerifyXsrfToken(base_settings.GET_PASSPHRASE_ACTION) if self.request.get('id'): try: entity = self.SECRET_MODEL.get(db.Key(self.request.get('id'))) except datastore_errors.BadKeyError: raise base.AccessError('target_id is malformed') else: entity = self.SECRET_MODEL.GetLatestForTarget(target_id, tag=self.request.get( 'tag', 'default')) if not entity: raise base.AccessError('Passphrase not found: target_id %s' % target_id) user = base.GetCurrentUser() self.CheckRetrieveAuthorization(entity=entity, user=user) self.AUDIT_LOG_MODEL.Log(message='GET', entity=entity, request=self.request) # Send retrieval email if user is not retrieving their own secret. if entity.owner != user.email: SendRetrievalEmail(self.PERMISSION_TYPE, entity, user) escrow_secret = str(entity.secret).strip() escrow_barcode_svg = None qr_img_url = None if self.QRCODE_DURING_PASSPHRASE_RETRIEVAL: if len(escrow_secret) <= 100: qr_img_url = ( 'https://chart.googleapis.com/chart?chs=245x245&cht=qr&chl=' + cgi.escape(escrow_secret)) recovery_str = self._PassphraseTypeName(entity) params = { 'volume_type': self.SECRET_MODEL.ESCROW_TYPE_NAME, 'volume_uuid': entity.target_id, 'qr_img_url': qr_img_url, 'escrow_secret': escrow_secret, 'checksum': entity.checksum, 'recovery_str': recovery_str, } params[self.JSON_SECRET_NAME] = escrow_secret if entity.active: entity.UpdateMutableProperty('force_rekeying', True) self.response.out.write(util.ToSafeJson(params))
def _VerifyEscrowPermission(self): try: base.GetCurrentUser() except errors.AccessDeniedError: pass else: return super(FirmwarePasswordHandler, self)._VerifyEscrowPermission() raise errors.AccessDeniedError
def _CreateNewSecretEntity(self, owner, volume_uuid, secret): user = base.GetCurrentUser() platform = self.request.get('platform') # Set default platform to Mac if not platform: platform = 'Mac' return models.ProvisioningVolume(owner=owner, volume_uuid=volume_uuid, passphrase=str(secret))
def testGetCurrentUser(self): self.testbed.setup_env(user_email='*****@*****.**', overwrite=True) user = base.GetCurrentUser() self.assertEqual('*****@*****.**', user.user.email()) self.assertEqual(0, len(user.bitlocker_perms)) self.assertEqual(0, len(user.duplicity_perms)) self.assertEqual(0, len(user.filevault_perms)) self.assertEqual(0, len(user.luks_perms))
def XsrfTokenGenerate(action, user=None, timestamp=None): """Generate an XSRF token.""" if not user: user = base.GetCurrentUser().email if not timestamp: timestamp = time.time() timestr = str(timestamp) secret = crypto.ENCRYPTION_KEY_TYPES[settings.KEY_TYPE_DEFAULT_XSRF]() h = hmac.new(secret, XSRF_DELIMITER.join((user, action, timestr))) return base64.urlsafe_b64encode(''.join( (h.digest(), XSRF_DELIMITER, timestr)))
def get(self, action=None): """Handles GET requests.""" if not action: self.error(httplib.NOT_FOUND) return try: email = base.GetCurrentUser().email except errors.AccessDeniedError: raise errors.AccessDeniedError self.response.headers['Content-Type'] = 'text/plain' self.response.out.write(util.XsrfTokenGenerate(action, user=email))
def get(self): """Handles GET requests.""" base_handler.VerifyPermissions(permissions.RETRIEVE_CREATED_BY, base.GetCurrentUser(), permissions.TYPE_PROVISIONING) volumes = ProvisioningVolumesForUser(users.get_current_user(), PROVISIONING_FILTER_SECONDS) volumes = [volume.ToDict() for volume in volumes] self.response.out.write(util.ToSafeJson(volumes))
def post(self, volume_key): """Handles POST requests.""" self.VerifyXsrfToken(base_settings.CHANGE_OWNER_ACTION) base_handler.VerifyPermissions( permissions.CHANGE_OWNER, base.GetCurrentUser(), permissions.TYPE_FILEVAULT) new_entity = self.entity.Clone() new_entity.owner = self.request.get('new_owner') new_entity.put() self.AUDIT_LOG_MODEL.Log( entity=self.entity, request=self.request, message=( 'Owner changed from "%s" to "%s"' % (self.entity.owner, new_entity.owner)))
def get(self, action=None): """Handles GET requests.""" if not action: self.error(httplib.NOT_FOUND) return try: base.GetCurrentUser() except base.AccessDeniedError: self.error(httplib.UNAUTHORIZED) return self.response.headers['Content-Type'] = 'text/plain' self.response.out.write(util.XsrfTokenGenerate(action))
def get(self, type_name, target_id): """Handles GET requests.""" user = base.GetCurrentUser() tag = self.request.get('tag', 'default') entity = models_util.TypeNameToModel(type_name).GetLatestForTarget( target_id, tag) if not entity: # TODO(b/35954370) notify users about missing passphrases. self.response.write(util.ToSafeJson(False)) return if entity.owner != user.email: logging.warning('owner mismatch %s %s', entity.owner, user.email) # Passphrase retrieval is necessary for rekeying so we abort. self.response.write(util.ToSafeJson(False)) return self.response.write(util.ToSafeJson(bool(entity.force_rekeying)))
def get(self, type_name, target_id): """Handles GET requests.""" user = base.GetCurrentUser() tag = self.request.get('tag', 'default') entity = models_util.TypeNameToModel( type_name).GetLatestForTarget(target_id, tag) if not entity: if memcache.Client().get(target_id, namespace='experimental_rekey'): logging.info('experimental_rekey %s', target_id) self.response.write(util.ToSafeJson('experimental')) return self.response.write(util.ToSafeJson(False)) return if entity.owner != user.email: logging.warning( 'owner mismatch %s %s', entity.owner, user.email) # Passphrase retrieval is necessary for rekeying so we abort. self.response.write(util.ToSafeJson(False)) return self.response.write(util.ToSafeJson(bool(entity.force_rekeying)))
def VerifyAllPermissionTypes(required_permission, user=None): """Verifies if a user has the required_permission for all permission types. Args: required_permission: permission string from permissions.*. user: optional, base.User entity; default current user. Returns: Dict. Keys are permissions.TYPES values, and value booleans, True when the user has the required_permission for the permission type, False otherwise. """ if user is None: user = base.GetCurrentUser() perms = {} for permission_type in permissions.TYPES: try: VerifyPermissions(required_permission, user, permission_type) perms[permission_type] = True except base.AccessDeniedError: perms[permission_type] = False # TODO(user): if use of this method widens, consider returning a # collections.namedtuple instead of a basic dict. return perms
def get(self): """Handles GET requests.""" if self.request.get('json', '0') != '1': search_type = self.request.get('search_type') field1 = urllib.quote(self.request.get('field1')) value1 = urllib.quote(self.request.get('value1').strip()) prefix_search = urllib.quote(self.request.get( 'prefix_search', '0')) if search_type and field1 and value1: self.redirect('/ui/#/search/%s/%s/%s/%s' % (search_type, field1, value1, prefix_search)) else: self.redirect('/ui/', permanent=True) return tag = self.request.get('tag', 'default') search_type = self.request.get('search_type') field1 = self.request.get('field1') value1 = self.request.get('value1').strip() prefix_search = self.request.get('prefix_search', '0') == '1' try: model = models_util.TypeNameToModel(search_type) except ValueError: raise passphrase_handler.InvalidArgumentError( 'Invalid search_type %s' % search_type) if not (field1 and value1): raise base_handler.InvalidArgumentError('Missing field1 or value1') # Get the user's search and retrieve permissions for all permission types. search_perms = base_handler.VerifyAllPermissionTypes( permissions.SEARCH) retrieve_perms = base_handler.VerifyAllPermissionTypes( permissions.RETRIEVE_OWN) retrieve_created = base_handler.VerifyAllPermissionTypes( permissions.RETRIEVE_CREATED_BY) # user is performing a search, ensure they have permissions. if (not search_perms.get(search_type) and not retrieve_perms.get(search_type) and not retrieve_created.get(search_type)): raise base.AccessDeniedError('User lacks %s permission' % search_type) try: passphrases = _PassphrasesForQuery(model, field1, value1, prefix_search) except ValueError: self.error(httplib.NOT_FOUND) return skipped = False if not search_perms.get(search_type): results_len = len(passphrases) email = base.GetCurrentUser().user.email() passphrases = [x for x in passphrases if x.owner == email] skipped = len(passphrases) != results_len too_many_results = len(passphrases) >= MAX_PASSPHRASES_PER_QUERY passphrases = [ v.ToDict(skip_secret=True) for v in passphrases if v.tag == tag ] if model.ALLOW_OWNER_CHANGE: for passphrase in passphrases: if not passphrase['active']: continue link = '/api/internal/change-owner/%s/%s/' % (search_type, passphrase['id']) passphrase['change_owner_link'] = link self.response.out.write( util.ToSafeJson({ 'passphrases': passphrases, 'too_many_results': too_many_results, 'results_access_warning': skipped, }))
def get(self): # pylint: disable=g-bad-name """Handles GET requests.""" # TODO(user): Users with retrieve_own should not need to search to # retrieve their escrowed secrets. if self.request.get('json', '0') != '1': search_type = self.request.get('search_type') field1 = urllib.quote(self.request.get('field1')) value1 = urllib.quote(self.request.get('value1').strip()) prefix_search = urllib.quote(self.request.get( 'prefix_search', '0')) if search_type and field1 and value1: self.redirect('/ui/#/search/%s/%s/%s/%s' % (search_type, field1, value1, prefix_search)) else: self.redirect('/ui/', permanent=True) return tag = self.request.get('tag', 'default') search_type = self.request.get('search_type') field1 = self.request.get('field1') value1 = self.request.get('value1').strip() prefix_search = self.request.get('prefix_search', '0') == '1' try: model = volumes.TypeNameToModel(search_type) except ValueError: raise handlers.InvalidArgumentError('Invalid search_type %s' % search_type) if not (field1 and value1): raise handlers.InvalidArgumentError('Missing field1 or value1') # Get the user's search and retrieve permissions for all permission types. search_perms = handlers.VerifyAllPermissionTypes(permissions.SEARCH) retrieve_perms = handlers.VerifyAllPermissionTypes( permissions.RETRIEVE_OWN) retrieve_created = handlers.VerifyAllPermissionTypes( permissions.RETRIEVE_CREATED_BY) # user is performing a search, ensure they have permissions. if (not search_perms.get(search_type) and not retrieve_perms.get(search_type) and not retrieve_created.get(search_type)): raise base.AccessDeniedError('User lacks %s permission' % search_type) # TODO(user): implement multi-field search by building query here # or better yet using JavaScript. q = '%s:%s' % (field1, value1) try: passphrases = _PassphrasesForQuery(model, q, prefix_search) except ValueError: self.error(httplib.NOT_FOUND) return if not search_perms.get(search_type): username = base.GetCurrentUser().user.nickname() passphrases = [x for x in passphrases if x.owner == username] passphrases = [ v.ToDict(skip_secret=True) for v in passphrases if v.tag == tag ] if model.ALLOW_OWNER_CHANGE: for passphrase in passphrases: if not passphrase['active']: continue link = '/api/internal/change-owner/%s/%s/' % (search_type, passphrase['id']) passphrase['change_owner_link'] = link self.response.out.write(util.ToSafeJson(passphrases))