Skip to content

Commit 3d77a5c

Browse files
authored
Samples for migrating from Python 2.7 runtime to Python 3.7 (GoogleCloudPlatform#3656)
* Initial commit for storage sample * Added Python 3.7 version * Starting urlfetch samples * requests samples work in 2.7 and 3.7 (gcloud deploy app app3.yaml for the Python 3.7 version) * urlfetch async sample Working in Python 3 App Engine * Working in Python 2 App Engine, too * Added storage test for migration * Use standard environment variable name for bucket * Added tests * WIP on urlfetch authentication * Added new authenticated replacement for urlfetch * Added a test * Incoming app identity verification sample * Added test * Added READMEs * Update README.md * Update main.py * Update main.py * Update main.py * Include exception in log message * Fixed missing newline at EOF * Removed unused import * Removed unneeded import * Minor lint cleanup * Removed unneeded import * Added EOF newline * Lint fix * Removed unused import requests * Removed unused import * Tell linter to ignore needed module import order * Reordered imports * Adding requirements-test to samples * Linting directive added * Replaced test requirements with Python 2.7 compatible one * Another missing requirements-test.txt added * More requirements file updates * Typo fixes * Migration cleanup for automated tests * Adjusted tests for Python 2.7 * Make lint happier * Add requests adapter * Needed stub to run in test environment * Trying to get test to run in this environment * WIP * WIP * WIP
1 parent 5348280 commit 3d77a5c

38 files changed

+768
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## App Engine to App Engine Request Sample
2+
3+
This sample application shows how an App Engine for Python 2.7 app can verify
4+
the caller's identity for a request from an App Engine for Python 2.7 or
5+
App Engine for Python 3.7 app.
6+
7+
Requests from an App Engine for Python 2.7 app that use `urlfetch` have
8+
a trustworthy `X-Appengine-Inbound-Appid` header that can be used to verify
9+
the calling app's identity.
10+
11+
Requests from an App Engine for Python 3.7 app that include an Authorization
12+
header with an ID token for the calling app's default service account can
13+
be used to verify those calling apps' identities.
14+
15+
The appengine/standard_python37/migration/urlfetch sample app can be used
16+
to make calls to this app with a valid Authorization header.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
runtime: python27
16+
threadsafe: yes
17+
api_version: 1
18+
19+
libraries:
20+
- name: ssl
21+
version: latest
22+
23+
handlers:
24+
- url: .*
25+
script: main.app
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START vendor]
16+
from google.appengine.ext import vendor
17+
18+
# Add any libraries installed in the "lib" folder.
19+
vendor.add('lib')
20+
# [END vendor]
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Authenticate requests coming from other App Engine instances.
17+
"""
18+
19+
# [START gae_python_app_identity_incoming]
20+
from google.oauth2 import id_token
21+
from google.auth.transport import requests
22+
23+
import logging
24+
import webapp2
25+
26+
27+
def get_app_id(request):
28+
# Requests from App Engine Standard for Python 2.7 will include a
29+
# trustworthy X-Appengine-Inbound-Appid. Other requests won't have
30+
# that header, as the App Engine runtime will strip it out
31+
incoming_app_id = request.headers.get(
32+
'X-Appengine-Inbound-Appid', None)
33+
if incoming_app_id is not None:
34+
return incoming_app_id
35+
36+
# Other App Engine apps can get an ID token for the App Engine default
37+
# service account, which will identify the application ID. They will
38+
# have to include at token in an Authorization header to be recognized
39+
# by this method.
40+
auth_header = request.headers.get('Authorization', None)
41+
if auth_header is None:
42+
return None
43+
44+
# The auth_header must be in the form Authorization: Bearer token.
45+
bearer, token = auth_header.split()
46+
if bearer.lower() != 'bearer':
47+
return None
48+
49+
try:
50+
info = id_token.verify_oauth2_token(token, requests.Request())
51+
service_account_email = info['email']
52+
incoming_app_id, domain = service_account_email.split('@')
53+
if domain != 'appspot.gserviceaccount.com': # Not App Engine svc acct
54+
return None
55+
else:
56+
return incoming_app_id
57+
except Exception as e:
58+
# report or log if desired, as here:
59+
logging.warning('Request has bad OAuth2 id token: {}'.format(e))
60+
return None
61+
62+
63+
class MainPage(webapp2.RequestHandler):
64+
allowed_app_ids = [
65+
'other-app-id',
66+
'other-app-id-2'
67+
]
68+
69+
def get(self):
70+
incoming_app_id = get_app_id(self.request)
71+
72+
if incoming_app_id is None:
73+
self.abort(403)
74+
75+
if incoming_app_id not in self.allowed_app_ids:
76+
self.abort(403)
77+
78+
self.response.write('This is a protected page.')
79+
80+
81+
app = webapp2.WSGIApplication([
82+
('/', MainPage)
83+
], debug=True)
84+
# [END gae_python_app_identity_incoming]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import webtest
16+
17+
import main
18+
19+
20+
def test_get():
21+
app = webtest.TestApp(main.app)
22+
23+
try:
24+
response = app.get('/')
25+
assert response.status_int == 403
26+
except webtest.app.AppError as e:
27+
assert '403 Forbidden' in str(e)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pytest>=4.6.10
2+
webtest>=2.0.35
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
google-auth>=1.14.1
2+
requests>=2.23.0
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## App Engine cloudstorage library Replacement
2+
3+
The runtime for App Engine standard for Python 2.7 includes the `cloudstorage`
4+
library, which is used to store and retrieve blobs. The sample in this
5+
directory shows how to do the same operations using Python libraries that
6+
work in either App Engine standard for Python runtime, version 2.7 or 3.7.
7+
The sample code is the same for each environment.
8+
9+
To deploy and run this sample in App Engine standard for Python 2.7:
10+
11+
pip install -t lib -r requirements.txt
12+
gcloud app deploy
13+
14+
To deploy and run this sample in App Engine standard for Python 3.7:
15+
16+
gcloud app deploy app3.yaml
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
runtime: python27
2+
api_version: 1
3+
threadsafe: yes
4+
5+
env_variables:
6+
CLOUD_STORAGE_BUCKET: "REPLACE THIS WITH YOUR EXISTING BUCKET NAME"
7+
BLOB_NAME: "my-demo-blob"
8+
9+
libraries:
10+
- name: ssl
11+
version: latest
12+
13+
handlers:
14+
- url: /.*
15+
script: main.app
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
runtime: python37
2+
3+
env_variables:
4+
CLOUD_STORAGE_BUCKET: "REPLACE THIS WITH YOUR EXISTING BUCKET NAME"
5+
BLOB_NAME: "my-demo-blob"

0 commit comments

Comments
 (0)