зеркало из https://github.com/mozilla/doorman.git
Simply and improve consistency
This commit is contained in:
Родитель
2cd6ba2119
Коммит
88373e250f
|
@ -5,7 +5,8 @@ Doorman
|
|||
|
||||
*Doorman* is an **authorization micro-service**.
|
||||
|
||||
[API Documentation](https://mozilla.github.io/doorman/)
|
||||
- [API Documentation](https://mozilla.github.io/doorman/)
|
||||
- [Examples](examples/)
|
||||
|
||||
[![Build Status](https://travis-ci.org/mozilla/doorman.svg?branch=master)](https://travis-ci.org/mozilla/doorman)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/mozilla/doorman/badge.svg?branch=master)](https://coveralls.io/github/mozilla/doorman?branch=master)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Doorman Integration Examples
|
||||
|
||||
Here are some examples of how to integrate Doorman with your service.
|
||||
- [Python / Flask](python/): A Web UI interacts with Auth0 and a Flask API
|
||||
|
||||
- [Python / Flask example](python/)
|
||||
|
|
|
@ -8,6 +8,4 @@ name = "pypi"
|
|||
[packages]
|
||||
|
||||
flask = "*"
|
||||
"python-dotenv" = "*"
|
||||
"python-jose" = "*"
|
||||
"flask-cors" = "*"
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "8945c85e8436bb5f825358403b459dd640c494b819b6f09a474653091c02f14e"
|
||||
"sha256": "f7aa6b6f4306185cc521b1df2b2db72cf6053464e44784087db3fa20208ca4f2"
|
||||
},
|
||||
"host-environment-markers": {
|
||||
"implementation_name": "cpython",
|
||||
"implementation_version": "3.5.3",
|
||||
"implementation_version": "3.6.3",
|
||||
"os_name": "posix",
|
||||
"platform_machine": "x86_64",
|
||||
"platform_python_implementation": "CPython",
|
||||
"platform_release": "4.10.0-33-generic",
|
||||
"platform_release": "4.13.0-16-generic",
|
||||
"platform_system": "Linux",
|
||||
"platform_version": "#37-Ubuntu SMP Fri Aug 11 10:55:28 UTC 2017",
|
||||
"python_full_version": "3.5.3",
|
||||
"python_version": "3.5",
|
||||
"platform_version": "#19-Ubuntu SMP Wed Oct 11 18:35:14 UTC 2017",
|
||||
"python_full_version": "3.6.3",
|
||||
"python_version": "3.6",
|
||||
"sys_platform": "linux"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
|
@ -34,13 +34,6 @@
|
|||
],
|
||||
"version": "==6.7"
|
||||
},
|
||||
"ecdsa": {
|
||||
"hashes": [
|
||||
"sha256:40d002cf360d0e035cf2cb985e1308d41aaa087cbfc135b2dc2d844296ea546c",
|
||||
"sha256:64cf1ee26d1cde3c73c6d7d107f835fed7c6a2904aef9eac223d57ad800c43fa"
|
||||
],
|
||||
"version": "==0.13"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
"sha256:0749df235e3ff61ac108f69ac178c9770caeaccad2509cb762ce1f65570a8856",
|
||||
|
@ -56,12 +49,6 @@
|
|||
],
|
||||
"version": "==3.0.3"
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
"sha256:e39ced1ab767b5936646cedba8bcce582398233d6a627067d4c6a454c90cfedb"
|
||||
],
|
||||
"version": "==0.16.0"
|
||||
},
|
||||
"itsdangerous": {
|
||||
"hashes": [
|
||||
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||
|
@ -70,10 +57,10 @@
|
|||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:2231bace0dfd8d2bf1e5d7e41239c06c9e0ded46e70cc1094a0aa64b0afeb054",
|
||||
"sha256:ddaa01a212cd6d641401cb01b605f4a4d9f37bfc93043d7f760ec70fb99ff9ff"
|
||||
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
||||
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
||||
],
|
||||
"version": "==2.9.6"
|
||||
"version": "==2.10"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
|
@ -81,26 +68,6 @@
|
|||
],
|
||||
"version": "==1.0"
|
||||
},
|
||||
"pycrypto": {
|
||||
"hashes": [
|
||||
"sha256:f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"
|
||||
],
|
||||
"version": "==2.6.1"
|
||||
},
|
||||
"python-dotenv": {
|
||||
"hashes": [
|
||||
"sha256:dc7940052cfe170e881aea40feb4ea7776e6a97170ed038fd2ee7e26e47585f2",
|
||||
"sha256:45e927c34204c90f5faa35ea8709b894f6b1a7712d77eb50940601068040993b"
|
||||
],
|
||||
"version": "==0.7.1"
|
||||
},
|
||||
"python-jose": {
|
||||
"hashes": [
|
||||
"sha256:fed56224664af0ebc3947853f1bed23b5609f90c7b02e3dce5ef5757d0301664",
|
||||
"sha256:18e19f200f37a8ee6247921572807cc63ee034abdbf6854df1ae7c1f505cabcc"
|
||||
],
|
||||
"version": "==1.4.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
|
||||
|
|
|
@ -1,28 +1,38 @@
|
|||
# Doorman, Python / Flask example with a Web UI
|
||||
# Doorman + Python API + Web UI
|
||||
|
||||
## How to run doorman?
|
||||
A Web UI interacts with Auth0 and a Flask API:
|
||||
|
||||
* Some views are protected by a Python decorator
|
||||
* The update view is protected by imperative code, where authors can only update their own records
|
||||
|
||||
## Run locally
|
||||
|
||||
Run those three services in separate terminals:
|
||||
|
||||
### Doorman
|
||||
|
||||
make serve -e POLICIES=examples/python/policies.yaml
|
||||
|
||||
### Flask API
|
||||
|
||||
## How to run the server
|
||||
We use [Pipenv](https://docs.pipenv.org) to ease packages installation.
|
||||
|
||||
cd examples/python/
|
||||
pipenv install
|
||||
export DOORMAN_SERVER=http://localhost:8080
|
||||
export API_AUDIENCE="SLocf7Sa1ibd5GNJMMqO539g7cKvWBOI"
|
||||
pipenv run python server.py
|
||||
|
||||
## How to run the web UI
|
||||
### Web UI
|
||||
|
||||
Because of Auth0 configuration, we must access the Web UI on http://iam.local:3000/
|
||||
|
||||
- Add this line to your `/etc/hosts`:
|
||||
|
||||
127.0.0.1 iam.local
|
||||
|
||||
- Serve the UI static files:
|
||||
|
||||
cd examples/python/ui/
|
||||
python3 -m http.server 3000
|
||||
|
||||
- Update your `/etc/hosts` so that you can resolve `iam.local`:
|
||||
|
||||
127.0.0.1 iam.local
|
||||
|
||||
- Access http://iam.local:3000/
|
||||
- Click **Login**
|
||||
|
|
|
@ -2,20 +2,15 @@ import json
|
|||
import urllib
|
||||
|
||||
|
||||
def json_dumps_ignore_none(d):
|
||||
return json.dumps({k: v for k, v in d.items() if v is not None})
|
||||
|
||||
|
||||
# Format error response and append status code.
|
||||
class AuthZError(Exception):
|
||||
def __init__(self, error, status_code):
|
||||
self.error = error
|
||||
self.status_code = status_code
|
||||
|
||||
|
||||
def allowed(server, audience, *,
|
||||
def allowed(doorman, service, *,
|
||||
resource=None, action=None, jwt=None, principals=None, context=None):
|
||||
doorman_url = server + "/allowed"
|
||||
doorman_url = doorman + "/allowed"
|
||||
payload = {
|
||||
"resource": resource,
|
||||
"action": action,
|
||||
|
@ -25,7 +20,7 @@ def allowed(server, audience, *,
|
|||
body = json_dumps_ignore_none(payload)
|
||||
headers = {
|
||||
"Authorization": jwt or '',
|
||||
"Origin": audience,
|
||||
"Origin": service,
|
||||
}
|
||||
r = urllib.request.Request(doorman_url, data=body.encode("utf-8"), headers=headers)
|
||||
try:
|
||||
|
@ -39,3 +34,7 @@ def allowed(server, audience, *,
|
|||
raise AuthZError(response_body, 403)
|
||||
|
||||
return response_body
|
||||
|
||||
|
||||
def json_dumps_ignore_none(d):
|
||||
return json.dumps({k: v for k, v in d.items() if v is not None})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
service: SLocf7Sa1ibd5GNJMMqO539g7cKvWBOI
|
||||
jwtIssuer: https://auth.mozilla.auth0.com/
|
||||
policies:
|
||||
- id: "1"
|
||||
- id: "hello"
|
||||
description: Allow everyone access hello
|
||||
principals:
|
||||
- <.*>
|
||||
|
@ -10,7 +10,7 @@ policies:
|
|||
resources:
|
||||
- hello
|
||||
effect: allow
|
||||
- id: "2"
|
||||
- id: "record-everyone"
|
||||
description: Allow everyone to list, read and create records
|
||||
principals:
|
||||
- <.*>
|
||||
|
@ -21,7 +21,7 @@ policies:
|
|||
resources:
|
||||
- record
|
||||
effect: allow
|
||||
- id: "3"
|
||||
- id: "record-authors"
|
||||
description: Allow authors to update their own record
|
||||
principals:
|
||||
- <.*>
|
||||
|
|
|
@ -13,14 +13,14 @@ import werkzeug
|
|||
import doorman
|
||||
|
||||
DOORMAN_SERVER = os.getenv("DOORMAN_SERVER", "http://localhost:8080")
|
||||
API_AUDIENCE = os.getenv("API_AUDIENCE")
|
||||
SERVICE = os.getenv("SERVICE", "SLocf7Sa1ibd5GNJMMqO539g7cKvWBOI")
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
RECORDS_PATH = os.getenv("RECORDS_PATH", os.path.join(HERE, "records"))
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
allowed = functools.partial(doorman.allowed, DOORMAN_SERVER, API_AUDIENCE)
|
||||
allowed = functools.partial(doorman.allowed, DOORMAN_SERVER, SERVICE)
|
||||
|
||||
|
||||
@app.errorhandler(doorman.AuthZError)
|
||||
|
@ -35,8 +35,8 @@ def authorized(**allowed_kw):
|
|||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
jwt = request.headers.get("Authorization", None)
|
||||
payload = allowed(jwt=jwt, **allowed_kw)
|
||||
_app_ctx_stack.top.current_user = payload
|
||||
authz = allowed(jwt=jwt, **allowed_kw)
|
||||
_app_ctx_stack.top.authz = authz
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
||||
return wrapped
|
||||
|
@ -49,31 +49,28 @@ def authorized(**allowed_kw):
|
|||
def hello():
|
||||
"""A valid access token is required to access this route
|
||||
"""
|
||||
top = _app_ctx_stack.top
|
||||
return jsonify(top.current_user)
|
||||
authz = _app_ctx_stack.top.authz
|
||||
return jsonify(authz)
|
||||
|
||||
|
||||
@app.route("/records")
|
||||
@cross_origin(headers=["Content-Type", "Authorization"])
|
||||
@cross_origin(headers=["Access-Control-Allow-Origin", "*"])
|
||||
@authorized(resource="record", action="list")
|
||||
def records():
|
||||
jwt = request.headers.get("Authorization", None)
|
||||
# Check if allowed to list.
|
||||
authz = allowed(resource="record", action="list", jwt=jwt)
|
||||
|
||||
authz = _app_ctx_stack.top.authz
|
||||
email_principal = authz["principals"][1]
|
||||
records = Records.list(email_principal)
|
||||
|
||||
records = Records.list(author=email_principal)
|
||||
return jsonify(records)
|
||||
|
||||
|
||||
@app.route("/records/<record_id>", methods=('GET', 'PUT'))
|
||||
@app.route("/records/<name>", methods=('GET', 'PUT'))
|
||||
@cross_origin(headers=["Content-Type", "Authorization"])
|
||||
@cross_origin(headers=["Access-Control-Allow-Origin", "*"])
|
||||
def record(record_id):
|
||||
def record(name):
|
||||
jwt = request.headers.get("Authorization", None)
|
||||
|
||||
record, author = Records.read(record_id)
|
||||
record, author = Records.read(name)
|
||||
|
||||
if request.method == "GET":
|
||||
action = "read"
|
||||
|
@ -89,9 +86,9 @@ def record(record_id):
|
|||
|
||||
# Save content on PUT
|
||||
if request.method == "PUT":
|
||||
record = request.get_json()
|
||||
body = request.data.decode("utf-8")
|
||||
email_principal = authz["principals"][1]
|
||||
Records.save(record_id, record, email_principal)
|
||||
record = Records.save(name, body, email_principal)
|
||||
|
||||
return jsonify(record)
|
||||
|
||||
|
@ -116,13 +113,14 @@ class Records:
|
|||
@staticmethod
|
||||
def save(name, body, author):
|
||||
path = os.path.join(RECORDS_PATH, "{}.json".format(os.path.basename(name)))
|
||||
body = {'body': body, 'author': author}
|
||||
with open(path, 'w') as f:
|
||||
body = {'body': body, 'author': author}
|
||||
json.dump(body, f)
|
||||
return body
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("RECORDS_PATH", RECORDS_PATH)
|
||||
print("DOORMAN_SERVER", DOORMAN_SERVER)
|
||||
print("API_AUDIENCE", API_AUDIENCE)
|
||||
print("SERVICE", SERVICE)
|
||||
app.run(host="0.0.0.0", port=os.getenv("PORT", 8000))
|
||||
|
|
|
@ -7,23 +7,37 @@
|
|||
<script type="text/javascript" src="main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Doorman + Python API + Frontend</h1>
|
||||
<h1>Doorman + Python API + Web UI</h1>
|
||||
<button id="login">Login</button>
|
||||
<button id="logout">Logout</button>
|
||||
<div id="view" class="tabs">
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-1" name="tab-group-1" checked>
|
||||
<label for="tab-1">Token payload</label>
|
||||
<input type="radio" id="tab-1" name="tab-group-1">
|
||||
<label for="tab-1">API Hello</label>
|
||||
|
||||
<div class="content">
|
||||
<div id="token-payload" class="pre"></div>
|
||||
<div id="api-hello" class="pre"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-2" name="tab-group-1">
|
||||
<label for="tab-2">User Info</label>
|
||||
<label for="tab-2">API Records</label>
|
||||
|
||||
<div class="content">
|
||||
<form id="api-record-form">
|
||||
<input type="text" name="name" placeholder="Name..." />
|
||||
<input type="text" name="data" placeholder="Some data..."></textarea>
|
||||
<input type="submit" value="Save"/>
|
||||
</form>
|
||||
<div id="api-records"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-3" name="tab-group-1">
|
||||
<label for="tab-3">User Info</label>
|
||||
|
||||
<div class="content">
|
||||
<div id="profile-nickname"></div>
|
||||
|
@ -33,25 +47,11 @@
|
|||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-3" name="tab-group-1">
|
||||
<label for="tab-3">API Hello</label>
|
||||
<input type="radio" id="tab-4" name="tab-group-1" checked>
|
||||
<label for="tab-4">Token payload</label>
|
||||
|
||||
<div class="content">
|
||||
<div id="api-hello" class="pre"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-4" name="tab-group-1">
|
||||
<label for="tab-4">API Records</label>
|
||||
|
||||
<div class="content">
|
||||
<form id="api-record-form">
|
||||
<input type="text" name="name" placeholder="Name..." />
|
||||
<textarea name="body" placeholder="Some JSON..."></textarea>
|
||||
<input type="submit" value="Save"/>
|
||||
</form>
|
||||
<div id="api-records"></div>
|
||||
<div id="token-payload" class="pre"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,10 +9,7 @@ const SCOPES = 'openid profile';
|
|||
document.addEventListener('DOMContentLoaded', main);
|
||||
|
||||
function main() {
|
||||
const logoutBtn = document.getElementById('logout');
|
||||
logoutBtn.addEventListener('click', logout);
|
||||
|
||||
const webAuth = new auth0.WebAuth({
|
||||
const webAuth0 = new auth0.WebAuth({
|
||||
domain: AUTH0_DOMAIN,
|
||||
clientID: AUTH0_CLIENT_ID,
|
||||
redirectUri: AUTH0_CALLBACK_URL,
|
||||
|
@ -20,57 +17,89 @@ function main() {
|
|||
scope: SCOPES
|
||||
});
|
||||
|
||||
// Authentication on Login button
|
||||
// Start authentication process on Login button
|
||||
const loginBtn = document.getElementById('login');
|
||||
loginBtn.addEventListener('click', () => {
|
||||
webAuth.authorize();
|
||||
webAuth0.authorize();
|
||||
});
|
||||
// Logout button.
|
||||
const logoutBtn = document.getElementById('logout');
|
||||
logoutBtn.addEventListener('click', logout);
|
||||
|
||||
// New record form.
|
||||
const newRecordForm = document.getElementById('api-record-form');
|
||||
newRecordForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(newRecordForm);
|
||||
await postNewRecords(formData.get('name'), formData.get('body'))
|
||||
// Empty form once submitted.
|
||||
newRecordForm.reset()
|
||||
});
|
||||
|
||||
handleAuthentication(webAuth)
|
||||
handleAuthentication(webAuth0)
|
||||
}
|
||||
|
||||
function handleAuthentication(webAuth) {
|
||||
webAuth.parseHash((err, authResult) => {
|
||||
class APIClient {
|
||||
constructor(auth) {
|
||||
const headers = {
|
||||
'Authorization': `${auth.tokenType} ${auth.idToken}`,
|
||||
};
|
||||
this.options = {headers};
|
||||
}
|
||||
|
||||
async hello() {
|
||||
const resp = await fetch(`${SERVICE_URL}/`, this.options);
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async list() {
|
||||
const resp = await fetch(`${SERVICE_URL}/records`, this.options);
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async save(name, body) {
|
||||
const resp = await fetch(`${SERVICE_URL}/records/${name}`,
|
||||
{method: 'PUT', body, ...this.options});
|
||||
return await resp.json();
|
||||
}
|
||||
}
|
||||
|
||||
function handleAuthentication(webAuth0) {
|
||||
let authenticated = false;
|
||||
|
||||
webAuth0.parseHash((err, authResult) => {
|
||||
if (authResult && authResult.accessToken && authResult.idToken) {
|
||||
// Token was passed in location hash by authentication portal.
|
||||
authenticated = true;
|
||||
window.location.hash = '';
|
||||
setSession(authResult);
|
||||
} else if (err) {
|
||||
// Server returned an error.
|
||||
console.error(err);
|
||||
alert(
|
||||
'Error: ' + err.error + '. Check the console for further details.'
|
||||
);
|
||||
alert(`Error: ${err.error}. Check the console for further details.`);
|
||||
} else {
|
||||
authResult = JSON.parse(sessionStorage.getItem('session'));
|
||||
// Look into session storage for session.
|
||||
const expiresAt = JSON.parse(sessionStorage.getItem('expires_at'));
|
||||
// Check whether the current time is past the access token's expiry time
|
||||
if (new Date().getTime() < expiresAt) {
|
||||
authenticated = true;
|
||||
authResult = JSON.parse(sessionStorage.getItem('session'));
|
||||
}
|
||||
}
|
||||
|
||||
displayButtons()
|
||||
// Show/hide menus.
|
||||
displayButtons(authenticated)
|
||||
|
||||
if (isAuthenticated()) {
|
||||
// Interact with API if authenticated.
|
||||
if (authenticated) {
|
||||
console.log('AuthResult', authResult);
|
||||
const tokenPayloadDiv = document.getElementById('token-payload');
|
||||
tokenPayloadDiv.innerText = JSON.stringify(authResult.idTokenPayload, null, 2);
|
||||
showTokenPayload(authResult)
|
||||
|
||||
const apiClient = new APIClient(authResult);
|
||||
|
||||
initRecordForm(apiClient)
|
||||
|
||||
Promise.all([
|
||||
fetchUserInfo(webAuth),
|
||||
showAPIHello(),
|
||||
showAPIRecords(),
|
||||
fetchUserInfo(webAuth0, authResult),
|
||||
showAPIHello(apiClient),
|
||||
showAPIRecords(apiClient),
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayButtons() {
|
||||
if (isAuthenticated()) {
|
||||
function displayButtons(authenticated) {
|
||||
if (authenticated) {
|
||||
document.getElementById('login').setAttribute('disabled', 'disabled');
|
||||
document.getElementById('logout').removeAttribute('disabled');
|
||||
document.getElementById('view').style.display = 'block';
|
||||
|
@ -90,28 +119,18 @@ function setSession(authResult) {
|
|||
sessionStorage.setItem('expires_at', expiresAt);
|
||||
}
|
||||
|
||||
function isAuthenticated() {
|
||||
// Check whether the current time is past the
|
||||
// access token's expiry time
|
||||
const expiresAt = JSON.parse(sessionStorage.getItem('expires_at'));
|
||||
return new Date().getTime() < expiresAt;
|
||||
}
|
||||
|
||||
function logout() {
|
||||
// Remove tokens and expiry time from sessionStorage
|
||||
sessionStorage.removeItem('session');
|
||||
sessionStorage.removeItem('expires_at');
|
||||
displayButtons();
|
||||
displayButtons(false);
|
||||
}
|
||||
|
||||
async function fetchUserInfo(webAuth) {
|
||||
const auth = JSON.parse(sessionStorage.getItem('session'));
|
||||
webAuth.client.userInfo(auth.accessToken, (err, profile) => {
|
||||
async function fetchUserInfo(webAuth0, auth) {
|
||||
webAuth0.client.userInfo(auth.accessToken, (err, profile) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
alert(
|
||||
'Error: ' + err.error + '. Check the console for further details.'
|
||||
);
|
||||
alert(`Error: ${err.error}. Check the console for further details.`);
|
||||
}
|
||||
document.getElementById('profile-nickname').innerText = profile.nickname;
|
||||
document.getElementById('profile-picture').setAttribute('src', profile.picture);
|
||||
|
@ -119,67 +138,48 @@ async function fetchUserInfo(webAuth) {
|
|||
});
|
||||
}
|
||||
|
||||
class APIClient {
|
||||
constructor() {
|
||||
const auth = JSON.parse(sessionStorage.getItem('session'));
|
||||
this.headers = {
|
||||
'Authorization': `${auth.tokenType} ${auth.idToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
}
|
||||
|
||||
async hello() {
|
||||
const resp = await fetch(`${SERVICE_URL}/`, {headers: this.headers});
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async list() {
|
||||
const resp = await fetch(`${SERVICE_URL}/records`, {headers: this.headers});
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async save(name, body) {
|
||||
const resp = await fetch(`${SERVICE_URL}/records/${name}`,
|
||||
{method: 'PUT', body, headers: this.headers});
|
||||
return await resp.json();
|
||||
}
|
||||
function showTokenPayload(auth) {
|
||||
const tokenPayloadDiv = document.getElementById('token-payload');
|
||||
tokenPayloadDiv.innerText = JSON.stringify(auth.idTokenPayload, null, 2);
|
||||
}
|
||||
|
||||
async function showAPIHello() {
|
||||
const c = new APIClient();
|
||||
const data = await c.hello();
|
||||
async function showAPIHello(apiClient) {
|
||||
const data = await apiClient.hello();
|
||||
|
||||
const apiHelloDiv = document.getElementById('api-hello');
|
||||
apiHelloDiv.innerText = JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
async function showAPIRecords() {
|
||||
const c = new APIClient();
|
||||
const data = await c.list();
|
||||
function initRecordForm(apiClient) {
|
||||
const newRecordForm = document.getElementById('api-record-form');
|
||||
// Submit data.
|
||||
newRecordForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(newRecordForm);
|
||||
await apiClient.save(formData.get('name'), formData.get('data'));
|
||||
// Empty form once submitted.
|
||||
newRecordForm.reset()
|
||||
// Refresh list.
|
||||
await showAPIRecords(apiClient);
|
||||
});
|
||||
}
|
||||
|
||||
async function showAPIRecords(apiClient) {
|
||||
const apiRecordsDiv = document.getElementById('api-records');
|
||||
apiRecordsDiv.innerHTML = '';
|
||||
|
||||
const data = await apiClient.list();
|
||||
if (data.length == 0) {
|
||||
apiRecordsDiv.innerText = 'No records';
|
||||
return
|
||||
}
|
||||
|
||||
apiRecordsDiv.innerHTML = '';
|
||||
for (const {name, body} of data) {
|
||||
const _name = document.createElement('h2');
|
||||
_name.innerText = name;
|
||||
const _body = document.createElement('p');
|
||||
_body.className = 'pre';
|
||||
_body.innerText = JSON.stringify(body, null, 2);
|
||||
_body.innerText = body;
|
||||
apiRecordsDiv.appendChild(_name);
|
||||
apiRecordsDiv.appendChild(_body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function postNewRecords(name, body) {
|
||||
const c = new APIClient();
|
||||
await c.save(name, body);
|
||||
// Refresh list.
|
||||
await showAPIRecords();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче