diff --git a/configs/config.dev.yaml b/configs/config.dev.yaml index 2447541..ace90a7 100644 --- a/configs/config.dev.yaml +++ b/configs/config.dev.yaml @@ -30,6 +30,9 @@ iris: twilio_messages: 'response/twilio/messages' twilio_status: 'twilio/deliveryupdate' slack: 'response/slack' +oncall: + host: localhost + port: 8080 iris-mobile: relay_app_name: 'iris-mobile' activated: True diff --git a/setup.py b/setup.py index d182652..e4965c8 100644 --- a/setup.py +++ b/setup.py @@ -33,21 +33,21 @@ 'pysaml2==4.8.0', 'PyYAML==5.1', 'gevent==1.4.0', - 'requests==2.20.1', + 'requests==2.23.0', 'requests-futures==0.9.9', - # TODO: update google client - 'google-api-python-client==1.4.2', + 'google-api-python-client==1.6.2', 'SQLAlchemy==1.2.0', 'PyMySQL==0.7.11', - 'oauth2client==1.4.12', + 'oauth2client==1.5.2', 'simplejson==3.8.1', 'slackclient==0.16', 'streql==3.0.2', 'twilio==6.25.0', - 'urllib3==1.24.2', + 'urllib3==1.25.6', 'falcon==1.1.0', 'ujson==1.35', - 'irisclient==1.3.0' + 'irisclient==1.3.0', + 'oncallclient==1.1.0', ], entry_points={ 'console_scripts': [ diff --git a/src/iris_relay/app.py b/src/iris_relay/app.py index 947980a..bb0aece 100644 --- a/src/iris_relay/app.py +++ b/src/iris_relay/app.py @@ -28,12 +28,12 @@ import os from saml2 import entity -from iris_relay.client import IrisClient +from iris_relay.client import IrisClient, OncallClient from iris_relay.gmail import Gmail from iris_relay.saml import SAML from irisclient import IrisClient as IrisMobileClient -from oncallclient import OncallClient +from oncallclient import OncallClient as OncallMobileClient logger = getLogger(__name__) @@ -207,6 +207,29 @@ def on_get(self, req, resp, idp_name): resp.status = falcon.HTTP_302 +class OncallCalendarRelay(object): + def __init__(self, config, oncall_client): + self.config = config + self.oncall_client = oncall_client + + def on_get(self, req, resp, ical_key): + """Access the oncall calendar identified by the key. + + The response is in ical format and this url is intended to be + supplied to any calendar application that can subscribe to + calendars from the internet. + """ + result = self.oncall_client.get('ical/' + ical_key) + if result.status == 200: + resp.status = falcon.HTTP_200 + elif 500 <= result.status <= 599: + resp.status = falcon.HTTP_503 + else: + resp.status = falcon.HTTP_404 + resp.content_type = result.headers['Content-Type'] + resp.body = result.data + + class GmailRelay(object): def __init__(self, config, iclient, gmail): self.config = config @@ -855,7 +878,7 @@ def process_request(self, req, resp): return else: raise falcon.HTTPUnauthorized('Authentication failure: server') - elif segments[2] == 'gmail' or segments[2] == 'gmail-oneclick' or segments[2] == 'slack': + elif segments[2] == 'gmail' or segments[2] == 'gmail-oneclick' or segments[2] == 'slack' or segments[2] == 'ical': return elif len(segments) == 1: if segments[0] == 'health' or segments[0] == 'healthcheck': @@ -933,6 +956,9 @@ def get_relay_app(config=None): if not config: config = read_config_from_argv() + oncall_client = OncallClient(config['oncall']['host'], + config['oncall']['port']) + iclient = IrisClient(config['iris']['host'], config['iris']['port'], config['iris'].get('relay_app_name', 'iris-relay'), @@ -944,6 +970,9 @@ def get_relay_app(config=None): cors = CORS(config.get('allow_origins_list', [])) app = falcon.API(middleware=[ReqBodyMiddleware(), AuthMiddleware(config), cors]) + ical_relay = OncallCalendarRelay(config, oncall_client) + app.add_route('/api/v0/ical/{ical_key}', ical_relay) + gmail_config = config.get('gmail') if gmail_config: gmail = Gmail(gmail_config, config.get('proxy')) @@ -979,7 +1008,7 @@ def get_relay_app(config=None): api_host=mobile_cfg['host'], key=mobile_cfg['api_key']) - mobile_oncall_client = OncallClient( + mobile_oncall_client = OncallMobileClient( app=mobile_cfg.get('relay_app_name', 'iris-relay'), key=mobile_cfg['oncall']['api_key'], api_host=mobile_cfg['oncall']['host']) diff --git a/src/iris_relay/bin/run_server.py b/src/iris_relay/bin/run_server.py index 0d8abb9..825b594 100755 --- a/src/iris_relay/bin/run_server.py +++ b/src/iris_relay/bin/run_server.py @@ -7,7 +7,6 @@ import yaml import multiprocessing import gunicorn.app.base -from gunicorn.six import iteritems class StandaloneApplication(gunicorn.app.base.BaseApplication): @@ -18,9 +17,9 @@ def __init__(self, options=None, skip_build_assets=False): super(StandaloneApplication, self).__init__() def load_config(self): - config = {key: value for key, value in iteritems(self.options) + config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None} - for key, value in iteritems(config): + for key, value in config.items(): self.cfg.set(key.lower(), value) def load(self): diff --git a/src/iris_relay/client.py b/src/iris_relay/client.py index 118e2c4..12aaa5b 100644 --- a/src/iris_relay/client.py +++ b/src/iris_relay/client.py @@ -17,7 +17,7 @@ class IrisClient(HTTPConnectionPool): def __init__(self, host, port, user, api_key, version=0, **kwargs): - super(IrisClient, self).__init__(host, port, **kwargs) + super().__init__(host, port, **kwargs) self.version = version self.user = user self.HMAC = hmac.new(api_key.encode('utf8'), b'', hashlib.sha512) @@ -67,3 +67,15 @@ def get(self, endpoint, params=None, raw=False): 'Authorization': auth_header.encode('utf8') + digest } return self.urlopen(method, path, headers=headers) + + +class OncallClient(HTTPConnectionPool): + def __init__(self, host, port, version=0, **kwargs): + super().__init__(host, port, **kwargs) + self.base_path = '/api/v%s/' % version + + def get(self, endpoint, params=None): + path = self.base_path + endpoint + if params: + path = ''.join([path, '?', urlencode(params)]) + return self.urlopen('GET', path)