[FIX] event: add signed static maps api
Google now requires an API key for the static maps API. As API calls cost money, and we cannot restrict the domain of the caller with the API key in emails, as the domain may vary vastly, the signing of static API URLs is implemented in the existing 'google_map_img' of partners. This in turn can be used to get a url for an existing location. While preventing anyone from stealing the API key for their own purposes task - 3079113 Part-of: odoo/odoo#107200
This commit is contained in:
@@ -225,7 +225,10 @@
|
||||
<div>
|
||||
<i class="fa fa-map-marker"/>
|
||||
<a t-attf-href="https://maps.google.com/maps?q={{ location }}" target="new">
|
||||
See location on Google Maps
|
||||
<img t-if="event_address.static_map_url and event_address.static_map_url_is_valid"
|
||||
t-att-src="event_address.static_map_url"
|
||||
style="vertical-align:bottom; width: 100%;" alt="Google Maps"/>
|
||||
<t t-else="">See location on Google Maps</t>
|
||||
</a>
|
||||
</div>
|
||||
</td></tr></table>
|
||||
@@ -442,11 +445,14 @@
|
||||
|
||||
<tr><td valign="top" style="font-size: 14px;">
|
||||
<!-- GOOGLE MAPS LINK -->
|
||||
<table t-if="event_address" style="width:100%;"><tr><td>
|
||||
<table t-if="event_address and location" style="width:100%;"><tr><td>
|
||||
<div>
|
||||
<i class="fa fa-map-marker"/>
|
||||
<a t-attf-href="https://maps.google.com/maps?q={{ location }}" target="new">
|
||||
See location on Google Maps
|
||||
<img t-if="event_address.static_map_url and event_address.static_map_url_is_valid"
|
||||
t-attf-src="{{ event_address.static_map_url }}"
|
||||
style="vertical-align:bottom; width: 100%;" alt="Google Maps"/>
|
||||
<span t-else="">See location on Google Maps</span>
|
||||
</a>
|
||||
</div>
|
||||
</td></tr></table>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
import base64
|
||||
import binascii
|
||||
import hmac
|
||||
|
||||
import requests
|
||||
import werkzeug.urls
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
@@ -9,14 +16,71 @@ class ResPartner(models.Model):
|
||||
|
||||
event_count = fields.Integer(
|
||||
'# Events', compute='_compute_event_count', groups='event.group_event_registration_desk')
|
||||
static_map_url = fields.Char(compute="_compute_static_map_url")
|
||||
static_map_url_is_valid = fields.Boolean(compute="_compute_static_map_url_is_valid")
|
||||
|
||||
def _compute_event_count(self):
|
||||
self.event_count = 0
|
||||
for partner in self:
|
||||
partner.event_count = self.env['event.event'].search_count([('registration_ids.partner_id', 'child_of', partner.ids)])
|
||||
|
||||
@api.depends('zip', 'city', 'country_id', 'street')
|
||||
def _compute_static_map_url(self):
|
||||
for partner in self:
|
||||
partner.static_map_url = partner._google_map_signed_img(zoom=13, width=598, height=200)
|
||||
|
||||
def _compute_static_map_url_is_valid(self):
|
||||
"""Compute whether the link is valid.
|
||||
|
||||
This should only remain valid for a relatively short time.
|
||||
Here, for the duration it is in cache.
|
||||
"""
|
||||
session = requests.Session()
|
||||
for partner in self:
|
||||
url = self.static_map_url
|
||||
if not url:
|
||||
partner.static_map_url_is_valid = False
|
||||
continue
|
||||
|
||||
is_valid = False
|
||||
# If the response isn't strictly successful, assume invalid url
|
||||
try:
|
||||
res = session.get(url, timeout=2)
|
||||
if res.ok and not res.headers.get('X-Staticmap-API-Warning'):
|
||||
is_valid = True
|
||||
except requests.exceptions.RequestException:
|
||||
pass
|
||||
|
||||
partner.static_map_url_is_valid = is_valid
|
||||
|
||||
def action_event_view(self):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("event.action_event_view")
|
||||
action['context'] = {}
|
||||
action['domain'] = [('registration_ids.partner_id', 'child_of', self.ids)]
|
||||
return action
|
||||
|
||||
def _google_map_signed_img(self, zoom=13, width=298, height=298):
|
||||
"""Create a signed static image URL for the location of this partner."""
|
||||
GOOGLE_MAPS_STATIC_API_KEY = self.env['ir.config_parameter'].sudo().get_param('google_maps.signed_static_api_key')
|
||||
GOOGLE_MAPS_STATIC_API_SECRET = self.env['ir.config_parameter'].sudo().get_param('google_maps.signed_static_api_secret')
|
||||
if not GOOGLE_MAPS_STATIC_API_KEY or not GOOGLE_MAPS_STATIC_API_SECRET:
|
||||
return None
|
||||
# generate signature as per https://developers.google.com/maps/documentation/maps-static/digital-signature#server-side-signing
|
||||
location_string = f"{self.street}, {self.city} {self.zip}, {self.country_id and self.country_id.display_name or ''}"
|
||||
params = {
|
||||
'center': location_string,
|
||||
'markers': f'size:mid|{location_string}',
|
||||
'size': f"{width}x{height}",
|
||||
'zoom': zoom,
|
||||
'sensor': "false",
|
||||
'key': GOOGLE_MAPS_STATIC_API_KEY,
|
||||
}
|
||||
unsigned_path = '/maps/api/staticmap?' + werkzeug.urls.url_encode(params)
|
||||
try:
|
||||
api_secret_bytes = base64.urlsafe_b64decode(GOOGLE_MAPS_STATIC_API_SECRET + "====")
|
||||
except binascii.Error:
|
||||
return None
|
||||
url_signature_bytes = hmac.digest(api_secret_bytes, unsigned_path.encode(), 'sha1')
|
||||
params['signature'] = base64.urlsafe_b64encode(url_signature_bytes)
|
||||
|
||||
return 'https://maps.googleapis.com/maps/api/staticmap?' + werkzeug.urls.url_encode(params)
|
||||
|
||||
Reference in New Issue
Block a user