Use a token to protect POST API queries
This commit is contained in:
parent
20a162e915
commit
c5183a570f
@ -49,6 +49,8 @@ adapt the behavior to your needs.
|
|||||||
to `/`). The value should end with a trailing slash.
|
to `/`). The value should end with a trailing slash.
|
||||||
* `THUNDERFOREST_API_KEY=` to pass an API key server to use for
|
* `THUNDERFOREST_API_KEY=` to pass an API key server to use for
|
||||||
[Thunderforest](http://thunderforest.com/) tiles (OpenCycleMap, etc).
|
[Thunderforest](http://thunderforest.com/) tiles (OpenCycleMap, etc).
|
||||||
|
* `API_TOKEN=` to pass a token required to access the server side API (check
|
||||||
|
below in the server part environment variables for more details).
|
||||||
|
|
||||||
You should also have a look at the build variables under the `config/`
|
You should also have a look at the build variables under the `config/`
|
||||||
subdirectory.
|
subdirectory.
|
||||||
@ -79,6 +81,7 @@ adapt its behavior:
|
|||||||
* `DATABASE=` to specify a [database URL](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#db-url) to connect to (defaults to
|
* `DATABASE=` to specify a [database URL](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#db-url) to connect to (defaults to
|
||||||
`sqlite:///reports.db` which means a SQLite database named `reports.db` in
|
`sqlite:///reports.db` which means a SQLite database named `reports.db` in
|
||||||
the current working directory).
|
the current working directory).
|
||||||
|
* `API_TOKEN=` to specify a token required to `POST` data to the API.
|
||||||
|
|
||||||
#### Serving in production
|
#### Serving in production
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
module.exports = {
|
module.exports = {
|
||||||
NODE_ENV: '"production"',
|
NODE_ENV: '"production"',
|
||||||
API_BASE_URL: JSON.stringify(process.env.API_BASE_URL),
|
API_BASE_URL: JSON.stringify(process.env.API_BASE_URL),
|
||||||
THUNDERFOREST_API_KEY: JSON.stringify(process.env.THUNDERFOREST_API_KEY),
|
API_TOKEN: JSON.stringify(process.env.API_TOKEN),
|
||||||
|
THUNDERFOREST_API_KEY: JSON.stringify(process.env.THUNDERFOREST_API_KEY),
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ def enable_cors():
|
|||||||
'PUT, GET, POST, DELETE, OPTIONS, PATCH'
|
'PUT, GET, POST, DELETE, OPTIONS, PATCH'
|
||||||
)
|
)
|
||||||
bottle.response.headers[str('Access-Control-Allow-Headers')] = str(
|
bottle.response.headers[str('Access-Control-Allow-Headers')] = str(
|
||||||
'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
|
'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token, '
|
||||||
|
'Authorization'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ Routes definitions
|
|||||||
"""
|
"""
|
||||||
import arrow
|
import arrow
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
import bottle
|
import bottle
|
||||||
|
|
||||||
@ -12,6 +13,29 @@ from server.models import Report
|
|||||||
from server import jsonapi
|
from server import jsonapi
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def check_auth():
|
||||||
|
"""
|
||||||
|
Check authentication.
|
||||||
|
|
||||||
|
:return: Abort and return a HTTP 403 page if authentication is not ok.
|
||||||
|
"""
|
||||||
|
if not os.getenv('API_TOKEN'):
|
||||||
|
return
|
||||||
|
|
||||||
|
auth = bottle.request.headers.get('Authorization', None)
|
||||||
|
if not auth:
|
||||||
|
raise AuthenticationError()
|
||||||
|
parts = auth.split()
|
||||||
|
if parts[0].lower() != 'bearer' or parts[1] != os.getenv('API_TOKEN'):
|
||||||
|
raise AuthenticationError()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def get_reports(only_active=False):
|
def get_reports(only_active=False):
|
||||||
"""
|
"""
|
||||||
Get reports for the reports getting routes.
|
Get reports for the reports getting routes.
|
||||||
@ -151,6 +175,12 @@ def post_report():
|
|||||||
if bottle.request.method == 'OPTIONS':
|
if bottle.request.method == 'OPTIONS':
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# Check authentication
|
||||||
|
try:
|
||||||
|
check_auth()
|
||||||
|
except AuthenticationError:
|
||||||
|
return jsonapi.JsonApiError(403, "Invalid authentication.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payload = json.load(bottle.request.body)
|
payload = json.load(bottle.request.body)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
@ -189,6 +219,12 @@ def upvote_report(id):
|
|||||||
if bottle.request.method == 'OPTIONS':
|
if bottle.request.method == 'OPTIONS':
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# Check authentication
|
||||||
|
try:
|
||||||
|
check_auth()
|
||||||
|
except AuthenticationError:
|
||||||
|
return jsonapi.JsonApiError(403, "Invalid authentication.")
|
||||||
|
|
||||||
r = Report.get(Report.id == id)
|
r = Report.get(Report.id == id)
|
||||||
if not r:
|
if not r:
|
||||||
return jsonapi.JsonApiError(404, "Invalid report id.")
|
return jsonapi.JsonApiError(404, "Invalid report id.")
|
||||||
@ -213,6 +249,12 @@ def downvote_report(id):
|
|||||||
if bottle.request.method == 'OPTIONS':
|
if bottle.request.method == 'OPTIONS':
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# Check authentication
|
||||||
|
try:
|
||||||
|
check_auth()
|
||||||
|
except AuthenticationError:
|
||||||
|
return jsonapi.JsonApiError(403, "Invalid authentication.")
|
||||||
|
|
||||||
r = Report.get(Report.id == id)
|
r = Report.get(Report.id == id)
|
||||||
if not r:
|
if not r:
|
||||||
return jsonapi.JsonApiError(404, "Invalid report id.")
|
return jsonapi.JsonApiError(404, "Invalid report id.")
|
||||||
|
@ -3,6 +3,10 @@ require('isomorphic-fetch');
|
|||||||
|
|
||||||
// With trailing slash
|
// With trailing slash
|
||||||
export const BASE_URL = process.env.API_BASE_URL || '/';
|
export const BASE_URL = process.env.API_BASE_URL || '/';
|
||||||
|
const AUTHORIZATION_HEADERS = new Headers({});
|
||||||
|
if (process.env.API_TOKEN) {
|
||||||
|
AUTHORIZATION_HEADERS.set('Authorization', `Bearer ${process.env.API_TOKEN}`);
|
||||||
|
}
|
||||||
|
|
||||||
export function saveReport(type, lat, lng) {
|
export function saveReport(type, lat, lng) {
|
||||||
return fetch(`${BASE_URL}api/v1/reports`, {
|
return fetch(`${BASE_URL}api/v1/reports`, {
|
||||||
@ -12,6 +16,7 @@ export function saveReport(type, lat, lng) {
|
|||||||
lat,
|
lat,
|
||||||
lng,
|
lng,
|
||||||
}),
|
}),
|
||||||
|
headers: AUTHORIZATION_HEADERS,
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
@ -34,6 +39,7 @@ export function getActiveReports() {
|
|||||||
export function downvote(id) {
|
export function downvote(id) {
|
||||||
return fetch(`${BASE_URL}api/v1/reports/${id}/downvote`, {
|
return fetch(`${BASE_URL}api/v1/reports/${id}/downvote`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: AUTHORIZATION_HEADERS,
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
@ -46,6 +52,7 @@ export function downvote(id) {
|
|||||||
export function upvote(id) {
|
export function upvote(id) {
|
||||||
return fetch(`${BASE_URL}api/v1/reports/${id}/upvote`, {
|
return fetch(`${BASE_URL}api/v1/reports/${id}/upvote`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: AUTHORIZATION_HEADERS,
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
|
Loading…
Reference in New Issue
Block a user