diff --git a/iap/refresh/.gitignore b/iap/refresh/.gitignore new file mode 100644 index 000000000000..a65b41774ad5 --- /dev/null +++ b/iap/refresh/.gitignore @@ -0,0 +1 @@ +lib diff --git a/iap/refresh/README.md b/iap/refresh/README.md new file mode 100644 index 000000000000..3f8471a13f72 --- /dev/null +++ b/iap/refresh/README.md @@ -0,0 +1,22 @@ +# Identity-Aware Proxy Refresh Session Sample + +This sample is used on the following documentation page: + +* https://cloud.google.com/iap/docs/sessions-howto + + +## Deploy to Google App Engine standard environment + +```shell +$ gcloud app deploy + +``` + +Enable Cloud IAP using the instructions here: +https://cloud.google.com/iap/docs/app-engine-quickstart#enabling_iap + +## Usage + +The app will continually refresh a fake status (always "Success"). After 1 hour, +the AJAX request will fail. The [js/poll.js](js/poll.js) code will detect this +and allow the user to refresh the session. diff --git a/iap/refresh/app.yaml b/iap/refresh/app.yaml new file mode 100644 index 000000000000..a057d5c7690d --- /dev/null +++ b/iap/refresh/app.yaml @@ -0,0 +1,9 @@ +runtime: python27 +threadsafe: yes +api_version: 1 + +handlers: +- url: /js + static_dir: js +- url: .* + script: main.app diff --git a/iap/refresh/appengine_config.py b/iap/refresh/appengine_config.py new file mode 100644 index 000000000000..c903d9a0ac5e --- /dev/null +++ b/iap/refresh/appengine_config.py @@ -0,0 +1,18 @@ +# Copyright 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. + +from google.appengine.ext import vendor + +# Add any libraries installed in the "lib" folder. +vendor.add('lib') diff --git a/iap/refresh/js/poll.js b/iap/refresh/js/poll.js new file mode 100644 index 000000000000..706f785b3df7 --- /dev/null +++ b/iap/refresh/js/poll.js @@ -0,0 +1,69 @@ +// Copyright Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +"use strict"; + +function getStatus() { + var statusElm = document.getElementById('status'); + statusElm.innerHTML = 'Polling'; + fetch('/status').then(function(response) { + if (response.ok) { + return response.text(); + } + // [START handle_error] + if (response.status === 401) { + statusElm.innerHTML = 'Login stale. '; + } + // [END handle_error] + else { + statusElm.innerHTML = response.statusText; + } + throw new Error (response.statusText); + }) + .then(function(text) { + statusElm.innerHTML = text; + }) + .catch(function(statusText) { + }); +} + +getStatus(); +setInterval(getStatus, 10000); // 10 seconds + +// [START refresh_session] +var iapSessionRefreshWindow = null; + +function sessionRefreshClicked() { + if (iapSessionRefreshWindow == null) { + iapSessionRefreshWindow = window.open("/_gcp_iap/do_session_refresh"); + window.setTimeout(checkSessionRefresh, 500); + } + return false; +} + +function checkSessionRefresh() { + if (iapSessionRefreshWindow != null && !iapSessionRefreshWindow.closed) { + fetch('/favicon.ico').then(function(response) { + if (response.status === 401) { + window.setTimeout(checkSessionRefresh, 500); + } else { + iapSessionRefreshWindow.close(); + iapSessionRefreshWindow = null; + } + }); + } else { + iapSessionRefreshWindow = null; + } +} +// [END refresh_session] diff --git a/iap/refresh/main.py b/iap/refresh/main.py new file mode 100644 index 000000000000..19edde411f58 --- /dev/null +++ b/iap/refresh/main.py @@ -0,0 +1,53 @@ +# Copyright Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. + +""" +Sample application that demonstrates refreshing a session when using +Identity Aware Proxy. This application is for App Engine Standard. +""" + +import flask +from google.appengine.api import users + +app = flask.Flask(__name__) + + +@app.route('/') +def index(): + user = users.get_current_user() + if user: + logged_in = True + nickname = user.nickname() + logout_url = users.create_logout_url('/') + login_url = None + else: + logged_in = False + nickname = None + logout_url = None + login_url = users.create_login_url('/') + + template_values = { + 'logged_in': logged_in, + 'nickname': nickname, + 'logout_url': logout_url, + 'login_url': login_url, + } + + return flask.render_template('index.html', **template_values) + + +# Fake status +@app.route('/status') +def status(): + return 'Success' diff --git a/iap/refresh/main_test.py b/iap/refresh/main_test.py new file mode 100644 index 000000000000..a6fb7e924ac1 --- /dev/null +++ b/iap/refresh/main_test.py @@ -0,0 +1,35 @@ +# Copyright Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. + +import main +import webtest + + +def test_index(testbed, login): + app = webtest.TestApp(main.app) + + response = app.get('/') + assert 'Login' in response.body + + login() + response = app.get('/') + assert 'Logout' in response.body + assert 'user@example.com' in response.body + + +def test_status(testbed): + app = webtest.TestApp(main.app) + + response = app.get('/status') + assert 'Success' in response.body diff --git a/iap/refresh/requirements.txt b/iap/refresh/requirements.txt new file mode 100644 index 000000000000..52ab1a390198 --- /dev/null +++ b/iap/refresh/requirements.txt @@ -0,0 +1 @@ +Flask==0.12.2 diff --git a/iap/refresh/templates/index.html b/iap/refresh/templates/index.html new file mode 100644 index 000000000000..3f6f84080a3b --- /dev/null +++ b/iap/refresh/templates/index.html @@ -0,0 +1,13 @@ + + +