def Post(self, domain, user): # pylint: disable=unused-argument """Creates, updates, or removes a catalog entry.""" label = self.request.get('label').strip() publisher_name = self.request.get('publisher_name').strip() if self.request.get('remove'): model.CatalogEntry.Delete(domain, label) self.redirect('.maps') else: if not re.match(r'^[\w-]+$', label): # Valid if alphanumeric, -, _ raise base_handler.Error( 400, 'Valid labels may only contain letters, digits, "-", and "_".' ) map_object = model.Map.Get(self.request.get('map')) if not map_object: raise base_handler.Error(400, 'No such map.') # Preserve the "is_listed" flag if the CatalogEntry already exists. entry = (model.CatalogEntry.Get(domain, label) or model.CatalogEntry.Create(domain, label, map_object)) entry.SetMapVersion(map_object) if publisher_name: entry.SetPublisherName(publisher_name) entry.Put() self.redirect('.maps')
def Get(self, label, user=None, domain=None): domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: raise base_handler.Error(404, 'No such map.') topics = entry.map_root.get('topics', []) if not topics: raise base_handler.Error(404, 'Map has no topics.') self.redirect('%s/%s' % (label, str(topics[0]['id'])))
def Get(self, map_id, topic_id, user=None, domain=None): m = model.Map.Get(map_id) if not m: logging.severe('No map with id %s' % map_id) raise base_handler.Error(404, 'No such map.') self.GetForMap(m.map_root, m.current_version_id, topic_id, None, domain)
def Get(self, map_id, domain=None): # pylint: disable=g-bad-name """Displays a map in draft mode by its map ID.""" map_object = model.Map.Get(map_id) if not map_object: raise base_handler.Error(404, 'Map %r not found.' % map_id) if not domain or domain != map_object.domain: # The canonical URL for a map contains both the domain and the map ID. url = '../%s/.maps/%s' % (map_object.domain, map_id) if self.request.GET: # preserve query params on redirect url += '?' + urllib.urlencode(self.request.GET.items()) return self.redirect(url) cm_config = GetConfig(self.request, map_object=map_object, xsrf_token=self.xsrf_token) # SECURITY NOTE: cm_config_json is assumed to be safe JSON, and head_html # is assumed to be safe HTML; all other template variables are autoescaped. # TODO(kpy): Factor out the bits common to MapByLabel.Get and MapById.Get. self.response.out.write(self.RenderTemplate('map.html', { 'maps_api_url': cm_config.pop('maps_api_url', ''), 'head_html': cm_config.pop('custom_head_html', ''), 'lang': cm_config['lang'], 'lang_lower': cm_config['lang'].lower().replace('-', '_'), 'cm_config_json': base_handler.ToHtmlSafeJson(cm_config) }))
def GetPlacesApiResults(base_url, request_params, result_key_name=None): """Fetches results from Places API given base_url and request params. Args: base_url: URL prefix to use before the request params request_params: An array of key and value pairs for the request result_key_name: Name of the results field in the Places API response or None if the whole response should be returned Returns: Value for the result_key_name in the Places API response or all of the response if result_key_name is None """ google_api_server_key = config.Get('google_api_server_key') if not google_api_server_key: raise base_handler.Error( 500, 'google_api_server_key is not set in the config') request_params += [('key', google_api_server_key)] url = base_url + urllib.urlencode([(k, v) for k, v in request_params if v]) # Call Places API if cache doesn't have a corresponding entry for the url def GetPlacesJson(): response = urlfetch.fetch(url=url, deadline=DEADLINE) return json.loads(response.content) response_content = JSON_PLACES_API_CACHE.Get(url, GetPlacesJson) # Parse results status = response_content.get('status') if status != 'OK' and status != 'ZERO_RESULTS': # Something went wrong with the request, log the error logging.error('Places API request [%s] failed with error %s', url, status) return [] return (response_content.get(result_key_name) if result_key_name else response_content)
def GetDomainAdmin(self, user, domain): # pylint:disable=unused-argument """Displays the administration page for the given domain.""" domain_name = domain perms.AssertAccess(perms.Role.DOMAIN_ADMIN, domain_name) domain = domains.Domain.Get(domain_name) if not domain: raise base_handler.Error(404, 'Unknown domain %r.' % domain_name) subject_roles = perms.GetSubjectsForTarget(domain_name) user_roles = [(users.Get(subj), _MaxRole(r)) for (subj, r) in subject_roles.items() if perms.IsUserId(subj)] user_roles.sort(key=lambda (u, r): u.email) labels = sorted(e.label for e in model.CatalogEntry.GetAll(domain_name)) self.response.out.write( self.RenderTemplate( 'admin_domain.html', { 'domain': domain, 'user_roles': user_roles, 'labels': labels, 'domain_role': _MaxRole( subject_roles.get(domain_name, set())), 'user_permission_choices': DOMAIN_PERMISSION_CHOICES, 'initial_domain_role_choices': INITIAL_DOMAIN_ROLE_CHOICES, 'show_welcome': self.request.get('welcome', '') }))
def Get(self, label, domain=None): # pylint: disable=g-bad-name """Displays a published map by its domain and publication label.""" domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: # Fall back to the map list for users that go to /crisismap/maps. # TODO(kpy): Remove this when the UI has a way to get to the map list. if label == 'maps': return self.redirect('.maps') raise base_handler.Error(404, 'Label %s/%s not found.' % (domain, label)) cm_config = GetConfig(self.request, catalog_entry=entry, xsrf_token=self.xsrf_token) map_root = cm_config.get('map_root', {}) # SECURITY NOTE: cm_config_json is assumed to be safe JSON, and head_html # is assumed to be safe HTML; all other template variables are autoescaped. # Below, we use cm_config.pop() for template variables that aren't part of # the API understood by google.cm.Map() and don't need to stay in cm_config. self.response.out.write(self.RenderTemplate('map.html', { 'maps_api_url': cm_config.pop('maps_api_url', ''), 'head_html': cm_config.pop('custom_head_html', ''), 'lang': cm_config['lang'], 'lang_lower': cm_config['lang'].lower().replace('-', '_'), 'json_proxy_url': cm_config['json_proxy_url'], 'maproot_url': cm_config.pop('maproot_url', ''), 'map_title': map_root.get('title', '') + ' | Google Crisis Map', 'map_description': ToPlainText(map_root.get('description')), 'map_url': self.request.path_url, 'map_image': map_root.get('thumbnail_url', ''), 'cm_config_json': base_handler.ToHtmlSafeJson(cm_config) }))
def Post(self, user, domain=''): # pylint: disable=unused-argument map_id = self.request.get('map') map_object = model.Map.Get(map_id) if not map_object: raise base_handler.Error(404, 'Map %r not found.' % map_id) map_object.Delete() self.redirect('.maps')
def Get(self, map_id): """Renders the admin page.""" perms.AssertAccess(perms.Role.ADMIN) map_object = model.Map.Get(map_id) or model.Map.GetDeleted(map_id) if not map_object: raise base_handler.Error(404, 'Map %r not found.' % map_id) self.response.out.write( self.RenderTemplate('admin_map.html', {'map': map_object}))
def Get(self, label, topic_id, user=None, domain=None): domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: logging.severe('No map with label %s under domain %s' % (label, domain)) raise base_handler.Error(404, 'No such map.') self.GetForMap(entry.map_root, entry.map_version_id, topic_id, label, domain)
def Post(self): _CheckIsDevServer() service_name = self.request.get('service', '') if service_name == 'urlshortener': self.response.out.write('{"id": "http://goo.gl/fakeShortUrl"}') return else: raise base_handler.Error(400, 'Unsupported test backend [%s]' % service_name)
def CreateDomain(self, domain_name, user): if domains.Domain.Get(domain_name): raise base_handler.Error(403, 'Domain %r already exists.' % domain_name) domains.Domain.Put(domain_name) utils.SetAndTest( lambda: perms.Grant(user.id, perms.Role.DOMAIN_ADMIN, domain_name), lambda: perms.CheckAccess(perms.Role.DOMAIN_ADMIN, domain_name, user))
def ParseJson(json_string): """Parses a JSON or JSONP string and returns the parsed object.""" match = JSON_CALLBACK_RE.match(json_string) if match: json_string = match.group( 1) # remove the function call around the JSON try: return json.loads(json_string) except (TypeError, ValueError): raise base_handler.Error(httplib.FORBIDDEN, 'Invalid JSON.')
def Get(self): _CheckIsDevServer() service_name = self.request.get('service', '') if service_name == 'file': # Fetch a static file and return it as an attachment in the response file_name = self.request.get('filename', '') if file_name[-4:] == '.kml': file_content = utils.ReadStaticFile(file_name) self.response.headers['Content-Type'] = KML_MIME_TYPE self.response.headers.add_header( 'content-disposition', 'attachment', filename=str(file_name)) self.response.out.write(file_content) return else: raise base_handler.Error( 400, 'Unsupported file type %s' % file_name[-4:]) else: raise base_handler.Error( 400, 'Unsupported test backend [%s]' % service_name)
def AddNewUserIfPresent(self, inputs, domain): """Grants domain roles to a new user.""" new_email = inputs.pop('new_user').strip() new_role = inputs.pop('new_user.permission') if not new_email or not new_role: return if not utils.IsValidEmail(new_email): raise base_handler.Error(400, 'Invalid e-mail address: %r.' % new_email) user = users.GetForEmail(new_email) perms.Grant(user.id, new_role, domain)
def AssertRateLimitNotExceeded(client_ip): """Raises an error if the given IP exceeds its allowed request rate.""" # We use memcache directly, as our cache abstraction does not support incr(), # but avoid key collision by using a Cache object to produce the cache keys. cache_key = QPM_CACHE.KeyToJson(client_ip) if memcache.get(cache_key) >= MAX_OUTBOUND_QPM_PER_IP: raise base_handler.Error( HTTP_TOO_MANY_REQUESTS, 'Rate limit exceeded; please try again later.') memcache.add(cache_key, 0, 60) memcache.incr(cache_key)
def _GetMap(self, label, domain): """Loads the model.Map instance being reviewed by label and domain. Args: label: A string, the published label for the map. domain: A string, the domain in which the map was created, eg gmail.com. Returns: The model.Map instance being reviewed Raises: base_handler.Error: If the map csnnot be found. """ domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: raise base_handler.Error(404, 'Map %r not found.' % label) map_object = model.Map.Get(entry.map_id) if not map_object: raise base_handler.Error(404, 'Map %r not found.' % label) return map_object
def Post(self, map_id, domain=None): # pylint: disable=unused-argument """Adds the recipient to the appropriate permission areas.""" map_object = model.Map.Get(map_id) if map_object is None: raise base_handler.Error(404, 'Map %r not found.' % map_id) role = self.request.get('role') recipient_email = self.request.get('recipient') message = self.request.get('message', '') # If these are empty or invalid, we shouldn't try to do anything. if role not in SHARING_ROLES: raise base_handler.Error(400, 'Invalid role parameter: %r.' % role) if not utils.IsValidEmail(recipient_email): raise base_handler.Error( 400, 'Invalid e-mail address: %r.' % recipient_email) # Change the recipient's permission level as specified. recipient = users.GetForEmail(recipient_email) map_object.ChangePermissionLevel(role, recipient.id) # Send the recipient an email. self.SendPermissionChangeEmail(recipient_email, map_object, role, message) self.response.set_status(201)
def Fetch(): """Fetch the URL and return the parsed JSON.""" AssertRateLimitNotExceeded(client_ip) method = post_json and 'POST' or 'GET' headers = post_json and {'Content-Type': 'application/json'} or {} if referrer: headers['Referer'] = referrer result = urlfetch.fetch(url, post_json, method, headers) if result.status_code != httplib.OK: logging.warn( 'Request for url=%r post_json=%r returned status %r: %r', url, post_json, result.status_code, result.content) raise base_handler.Error(result.status_code, 'Request failed.') return ParseJson(result.content)
def Get(self): """Returns legend items extracted from kml at given URL.""" url = jsonp.SanitizeUrl(self.request.get('url')) kml = self.GetKmlFromUrl(url) if kml is None: raise base_handler.Error(400, 'Failed to get KML from ' + url) (icon_styles, line_styles, polygon_styles, static_icon_urls, colors) = Extract(kml) self.WriteJson({ 'icon_styles': icon_styles, 'line_styles': line_styles, 'polygon_styles': polygon_styles, 'static_icon_urls': list(static_icon_urls), 'colors': list(colors) })
def _GetMap(self, map_id): """Loads the model.Map instance being reviewed by ID. Args: map_id: A string, the id of the map being reviewed. Returns: The model.Map instance being reviewed Raises: base_handler.Error: If the map csnnot be found. """ map_object = model.Map.Get(map_id) if not map_object: raise base_handler.Error(404, 'Map %r not found.' % map_id) return map_object
def SanitizeUrl(url): """Checks and returns a URL that is safe to fetch, or raises an error. Args: url: A URL. Returns: The URL, only if it is considered safe to fetch. Raises: base_handler.Error: The URL was missing or not safe to fetch. """ scheme, netloc, path, query, _ = urlparse.urlsplit(url) if scheme in ['http', 'https']: return urlparse.urlunsplit((scheme, netloc, path, query, '')) raise base_handler.Error(httplib.BAD_REQUEST, 'Missing or invalid URL.')
def Post(self, map_id): """Handles a POST (block/unblock, delete/undelete, or wipe).""" perms.AssertAccess(perms.Role.ADMIN) map_object = model.Map.Get(map_id) or model.Map.GetDeleted(map_id) if not map_object: raise base_handler.Error(404, 'Map %r not found.' % map_id) if self.request.get('block'): map_object.SetBlocked(True) if self.request.get('unblock'): map_object.SetBlocked(False) if self.request.get('delete'): map_object.Delete() if self.request.get('undelete'): map_object.Undelete() if self.request.get('wipe'): map_object.Wipe() self.redirect(map_id)
def Post(self, user, domain): """Landing for posts from the domain administration page.""" which = self.request.POST.pop('form') target = self.request.path if which != 'create-domain': perms.AssertAccess(perms.Role.DOMAIN_ADMIN, domain, user) if not domains.Domain.Get(domain): raise base_handler.Error(404, 'Unknown domain %r.' % domain) if which == 'domain-settings': self.UpdateDomainSettings(self.request.POST, domain) elif which == 'create-domain': self.CreateDomain(domain, user) target += '?welcome=1' else: # user or domain permissions inputs = dict(self.request.POST) self.AddNewUserIfPresent(inputs, domain) # Set access to this domain for all users with this e-mail domain. self.UpdateDomainRole(inputs, domain) # Set access to this domain for individually specified users. SetRolesForDomain(self.FindNewPerms(inputs), domain) self.redirect(target)
def _CheckIsDevServer(): if not utils.IsDevelopmentServer(): raise base_handler.Error(500, 'testbackend is only accessible in DEV')
def GetForMap(self, map_root, map_version_id, topic_id, map_label=None): """Renders the card for a particular map and topic. Args: map_root: The MapRoot dictionary for the map. map_version_id: The version ID of the MapVersionModel (for a cache key). topic_id: The topic ID. map_label: The label of the published map (for analytics). """ topic = GetTopic(map_root, topic_id) if not topic: raise base_handler.Error(404, 'No such topic.') output = str(self.request.get('output', '')) lat_lon = str(self.request.get('ll', '')) max_count = int(self.request.get('n', 5)) # number of results to show radius = float(self.request.get('r', 100000)) # radius, metres unit = str(self.request.get('unit', self.GetDistanceUnitForCountry())) qids = self.request.get('qids').replace(',', ' ').split() places_json = self.request.get('places') or '[]' place_id = str(self.request.get('place', '')) footer_json = self.request.get('footer') or '[]' location_unavailable = self.request.get('location_unavailable') lang = base_handler.SelectLanguageForRequest(self.request, map_root) include_descriptions = self.request.get('show_desc') try: places = json.loads(places_json) except ValueError: logging.error('Could not parse ?places= parameter') try: footer = json.loads(footer_json) except ValueError: logging.error('Could not parse ?footer= parameter') # If '?ll' parameter is supplied, find nearby results. center = None if lat_lon: try: lat, lon = lat_lon.split(',') center = ndb.GeoPt(float(lat), float(lon)) except ValueError: logging.error('Could not extract center for ?ll parameter') # If neither '?ll' nor '?place' parameters are given, or if ?place # value is invalid, use a default place. place = None if not center: place = {p['id']: p for p in places}.get(place_id, places and places[0]) # If '?place' parameter is supplied, use it as the center of the # point-radius query. if not center and place: try: lat, lon = place['ll'] center = ndb.GeoPt(lat, lon) except (KeyError, TypeError, ValueError): logging.error('Could not extract center for ?place=%s', place_id) try: # Find POIs associated with the topic layers features = GetFilteredFeatures( map_root, map_version_id, topic_id, self.request, center, radius, max_count) html_attrs = GetFeatureAttributions(features) SetAnswersAndReportsOnFeatures(features, map_root, topic_id, qids) geojson = GetGeoJson(features, include_descriptions) geojson['properties'] = { 'map_id': map_root.get('id'), 'topic': topic, 'html_attrs': html_attrs, 'unit': unit } if output == 'json': self.WriteJson(geojson) else: self.response.out.write(self.RenderTemplate('card.html', { 'features': geojson['features'], 'title': topic.get('title', ''), 'unit': unit, 'lang': lang, 'url_no_unit': RemoveParamsFromUrl(self.request.url, 'unit'), 'place': place, 'config_json': json.dumps({ 'url_no_loc': RemoveParamsFromUrl( self.request.url, 'll', 'place'), 'place': place, 'location_unavailable': bool(location_unavailable), 'map_id': map_root.get('id', ''), 'topic_id': topic_id, 'map_label': map_label or '', 'topic_title': topic.get('title', '') }), 'places_json': json.dumps(places), 'footer_html': RenderFooter(footer or [], html_attrs) })) except Exception, e: # pylint:disable=broad-except logging.exception(e)
def Get(self, map_id, topic_id, user=None, domain=None): m = model.Map.Get(map_id) if not m: raise base_handler.Error(404, 'No such map.') self.GetForMap(m.map_root, m.current_version_id, topic_id)
def CheckAccess(self): """Ensures this page is only accessible to developers.""" if not users.IsDeveloper(): raise base_handler.Error(404, 'Not found.')
def GetForMap(self, map_root, map_version_id, topic_id, map_label=None, domain=None): """Renders the card for a particular map and topic. Args: map_root: The MapRoot dictionary for the map. map_version_id: The version ID of the MapVersionModel (for a cache key). topic_id: The topic ID. map_label: The label of the published map (for analytics). domain: Owner domain of the map """ topic = GetTopic(map_root, topic_id) if not topic: raise base_handler.Error(404, 'No such topic.') lat_lon = str(self.request.get('ll', '')) max_count = int(self.request.get('n', 5)) # number of results to show radius = float(self.request.get('r', 100000)) # radius, metres unit = str(self.request.get('unit', self.GetDistanceUnitForCountry())) qids = self.request.get('qids').replace(',', ' ').split() places_json = self.request.get('places') or '[]' place_id = str(self.request.get('place', '')) include_descriptions = int(self.request.get('show_desc', 0)) include_crowd_reports = int(self.request.get('show_reports', 0)) try: places = json.loads(places_json) except ValueError: logging.error('Could not parse ?places= parameter') # If '?ll' parameter is supplied, find nearby results. center = None if lat_lon: try: lat, lon = lat_lon.split(',') center = ndb.GeoPt(float(lat), float(lon)) except ValueError: logging.error('Could not extract center for ?ll parameter') # If neither '?ll' nor '?place' parameters are given, or if ?place # value is invalid, use a default place. place = None if not center: place = {p['id']: p for p in places}.get(place_id, places and places[0]) # If '?place' parameter is supplied, use it as the center of the # point-radius query. if not center and place: try: lat, lon = place['ll'] center = ndb.GeoPt(lat, lon) except (KeyError, TypeError, ValueError): logging.error('Could not extract center for ?place=%s', place_id) try: # Find POIs associated with the topic layers features = GetFilteredFeatures(map_root, map_version_id, topic_id, self.request, center, radius, max_count) html_attrs = GetCardLevelAttributions(features) if include_crowd_reports: SetAnswersAndReportsOnFeatures(features, map_root, topic_id, qids) geojson = GetGeoJson(features, include_descriptions) geojson['properties'] = { 'map_id': map_root.get('id'), 'topic': topic, 'html_attrs': html_attrs, 'map_url': self.GetMapUrl(topic, map_label, domain, features), 'unit': unit } self.WriteJson(geojson) except Exception, e: # pylint:disable=broad-except logging.exception(e)