summaryrefslogtreecommitdiff
path: root/api/opendc/util/rest.py
diff options
context:
space:
mode:
authorGeorgios Andreadis <info@gandreadis.com>2020-07-20 12:44:04 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2020-08-24 19:48:12 +0200
commit53e60ccf0636e0076837d66a7dbea527e3b6e98d (patch)
tree041f83ae919d7ee9b5691a1666dbb61af26967d0 /api/opendc/util/rest.py
parentd8479e7e3744b8d1d31ac4d9f972e560eacd2cf8 (diff)
parent2a5f50e591f5e9c1da5db2f2011c779a88121675 (diff)
Merge pull request #9 from atlarge-research/feat/opendc-node
Add simulator integration
Diffstat (limited to 'api/opendc/util/rest.py')
-rw-r--r--api/opendc/util/rest.py141
1 files changed, 141 insertions, 0 deletions
diff --git a/api/opendc/util/rest.py b/api/opendc/util/rest.py
new file mode 100644
index 00000000..abd2f3de
--- /dev/null
+++ b/api/opendc/util/rest.py
@@ -0,0 +1,141 @@
+import importlib
+import json
+import os
+
+from oauth2client import client, crypt
+
+from opendc.util import exceptions, parameter_checker
+from opendc.util.exceptions import ClientError
+
+
+class Request:
+ """WebSocket message to REST request mapping."""
+ def __init__(self, message=None):
+ """"Initialize a Request from a socket message."""
+
+ # Get the Request parameters from the message
+
+ if message is None:
+ return
+
+ try:
+ self.message = message
+
+ self.id = message['id']
+
+ self.path = message['path']
+ self.method = message['method']
+
+ self.params_body = message['parameters']['body']
+ self.params_path = message['parameters']['path']
+ self.params_query = message['parameters']['query']
+
+ self.token = message['token']
+
+ except KeyError as exception:
+ raise exceptions.MissingRequestParameterError(exception)
+
+ # Parse the path and import the appropriate module
+
+ try:
+ self.path = message['path'].strip('/')
+
+ module_base = 'opendc.api.{}.endpoint'
+ module_path = self.path.replace('{', '').replace('}', '').replace('/', '.')
+
+ self.module = importlib.import_module(module_base.format(module_path))
+ except ImportError as e:
+ print(e)
+ raise exceptions.UnimplementedEndpointError('Unimplemented endpoint: {}.'.format(self.path))
+
+ # Check the method
+
+ if self.method not in ['POST', 'GET', 'PUT', 'PATCH', 'DELETE']:
+ raise exceptions.UnsupportedMethodError('Non-rest method: {}'.format(self.method))
+
+ if not hasattr(self.module, self.method):
+ raise exceptions.UnsupportedMethodError('Unimplemented method at endpoint {}: {}'.format(
+ self.path, self.method))
+
+ # Verify the user
+
+ if "OPENDC_FLASK_TESTING" in os.environ:
+ self.google_id = 'test'
+ return
+
+ try:
+ self.google_id = self._verify_token(self.token)
+ except crypt.AppIdentityError as e:
+ raise exceptions.AuthorizationTokenError(e)
+
+ def check_required_parameters(self, **kwargs):
+ """Raise an error if a parameter is missing or of the wrong type."""
+
+ try:
+ parameter_checker.check(self, **kwargs)
+ except exceptions.ParameterError as e:
+ raise ClientError(Response(400, str(e)))
+
+ def process(self):
+ """Process the Request and return a Response."""
+
+ method = getattr(self.module, self.method)
+
+ try:
+ response = method(self)
+ except ClientError as e:
+ e.response.id = self.id
+ return e.response
+
+ response.id = self.id
+
+ return response
+
+ def to_JSON(self):
+ """Return a JSON representation of this Request"""
+
+ self.message['id'] = 0
+ self.message['token'] = None
+
+ return json.dumps(self.message)
+
+ @staticmethod
+ def _verify_token(token):
+ """Return the ID of the signed-in user.
+
+ Or throw an Exception if the token is invalid.
+ """
+
+ try:
+ id_info = client.verify_id_token(token, os.environ['OPENDC_OAUTH_CLIENT_ID'])
+ except Exception as e:
+ print(e)
+ raise crypt.AppIdentityError('Exception caught trying to verify ID token: {}'.format(e))
+
+ if id_info['aud'] != os.environ['OPENDC_OAUTH_CLIENT_ID']:
+ raise crypt.AppIdentityError('Unrecognized client.')
+
+ if id_info['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
+ raise crypt.AppIdentityError('Wrong issuer.')
+
+ return id_info['sub']
+
+
+class Response:
+ """Response to websocket mapping"""
+ def __init__(self, status_code, status_description, content=None):
+ """Initialize a new Response."""
+
+ self.id = 0
+ self.status = {'code': status_code, 'description': status_description}
+ self.content = content
+
+ def to_JSON(self):
+ """"Return a JSON representation of this Response"""
+
+ data = {'id': self.id, 'status': self.status}
+
+ if self.content is not None:
+ data['content'] = self.content
+
+ return json.dumps(data)