diff --git a/README.txt b/README.txt index 7064491..e69de29 100644 --- a/README.txt +++ b/README.txt @@ -1 +0,0 @@ -# TBD diff --git a/etc/notifserver/development.ini b/etc/notifserver/development.ini new file mode 100644 index 0000000..dfee52b --- /dev/null +++ b/etc/notifserver/development.ini @@ -0,0 +1,60 @@ +[DEFAULT] +debug = False +translogger = False +profile = False + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 8000 +use_threadpool = True +threadpool_workers = 60 + +[app:main] +use = egg:NotifServer +configuration = file://%(here)s/notifserver.ini + +[cef] +use = false +file = syslog +vendor = mozilla +version = 0 +device_version = 1.3 +product = weave + +[logging] +enabled = true +server_log = /tmp/notifs.log +level = DEBUG + +[loggers] +keys = root,app + +[handlers] +keys = file01 + +[formatters] +keys = format01 + +[logger_root] +level = DEBUG +handlers = file01 + +[logger_app] +level = DEBUG +qualname = app +handlers = file01 +propgate = 0 + +[handler_file01] +class = FileHandler +level = DEBUG +formatter = format01 +args = ('/tmp/notifs.log', 'w') + +[formatter_format01] +format = %(name)s: %(asctime)s %(levelname)s %(message)s +datefmt = +class = logging.Formatter + + diff --git a/notifserver/auth/browserid.py b/notifserver/auth/browserid.py index 114b590..fb04ee8 100644 --- a/notifserver/auth/browserid.py +++ b/notifserver/auth/browserid.py @@ -11,15 +11,25 @@ class NotifServerAuthentication(object): """ for this class, username = user's email address, password = assertion """ + def __init__(self, *args, **kw): + self.config = kw.get('config', {}) + self.environ = kw.get('environ', {}) + def authenticate_user(self, user_name, password): """ Return a validated user id """ - try: - import pdb; pdb.set_trace() raw_assertion = password; jws = JWS(config = self.config, environ = {} ) assertion = jws.parse(raw_assertion) + + if (user_name != assertion.get('email')): + log_cef('username does not match assertion value %s != %s ' % + (user_name, assertion.get('email')), + 5, + environ = self.environ, + config = self.config) + raise HTTPUnauthorized("Invalid assertion") return assertion except JWSException, e: diff --git a/notifserver/auth/jws.py b/notifserver/auth/jws.py index 2ae6762..c753dad 100644 --- a/notifserver/auth/jws.py +++ b/notifserver/auth/jws.py @@ -35,7 +35,6 @@ from M2Crypto import RSA, BIO, ASN1 from hashlib import sha256, sha384, sha512 - import base64 import cef import hmac @@ -99,7 +98,7 @@ def parse(self, jws, **kw): raise(JWSException("Cannot verify empty JWS")) if self.verify(jws): (head, payload_str, signature) = jws.split('.') - payload = json.parse(base64.b64decode(payload_str)) + payload = json.loads(base64.b64decode(_check_b64pad(payload_str))) return payload else: raise(JWSException("Invalid JWS")) @@ -109,7 +108,7 @@ def verify(self, jws, alg = None, **kw): raise (JWSException("Cannot verify empty JWS")) try: (header_str, payload_str, signature) = jws.split('.') - header = json.parse(base64.b64decode(header_str)) + header = json.loads(base64.b64decode(_check_b64pad(header_str))) if alg is None: alg = header.get('alg', 'NONE') try: @@ -175,6 +174,8 @@ def _verify_HS(self, alg, header, sbs, signature): return (base64.urlsafe_b64encode(tsignature)) == signature def _verify_RS(self, alg, header, sbs, signature, testKey=None): + #TODO: Actually verify this by pulling the right keys from sig host + return True #rsa.verify(sbs, pubic_key) ## fetch the public key if testKey: @@ -207,8 +208,12 @@ def header(self, header = None, alg = None, **kw): return self._header +def _check_b64pad(string): + pad_size = 4 - (len(string) % 4) + return string + ('=' * pad_size) + def create_rsa_jki_entry(pubKey, keyid=None): - keys = json.parse(base64.b64decode(pubKey)) + keys = json.loads(base64.b64decode(_check_b64pad(pubKey))) vinz = {'algorithm': 'RSA', 'modulus': keys.get('n'), 'exponent': keys.get('e')} @@ -236,7 +241,7 @@ def fetch_rsa_pub_key(header, **kw): elif 'jku' in header and header.get('jku', None): key = header['jku'] if key.lower().startswith('data:'): - pub = json.parse(key[key.index('base64,')+7:]) + pub = json.loads(key[key.index('base64,')+7:]) return pub "" pub = { diff --git a/notifserver/controllers/clientagent.py b/notifserver/controllers/clientagent.py index 79d7012..23edd80 100644 --- a/notifserver/controllers/clientagent.py +++ b/notifserver/controllers/clientagent.py @@ -39,8 +39,6 @@ import json import logging -import pdb - from services.formatters import json_response from webob.exc import HTTPOk, HTTPBadRequest, HTTPInternalServerError from notifserver.controllers import BaseController @@ -59,25 +57,42 @@ class ClientAgent(BaseController): def _init(self, config): self.msg_backend = get_message_backend(config) + self.auth = self.app.auth.backend + self.auth.__init__(config = config) + + def _auth(self, request): + username = request.params.get('username') + password = request.params.get('password') + self.auth.authenticate_user(username,password); + request.environ['beaker.session']['uid'] = username + request.environ['beaker.session']['assertion'] = password + + def _get_uid(self, request): + uid = request.environ.get('beaker.session', {}).get('uid', None) + if uid is None: + self._auth(request) + return request.environ.get('beaker.session', {}).get('uid', None) def new_queue(self, request): """ Create a new queue for the user. (queues hold subscriptions) """ - import pdb; pdb.set_trace(); self._init(self.app.config) + username = self._get_uid(request) + #TODO: Authenticate the user using the backend auth. try: result = self.msg_backend.create_client_queue(username) return json_response(result) except Exception, e: - pdb.set_trace() - logger.error("Error creating client queue %s", str(e)) + logger.error("Error creating client queue %s" % str(e)) raise HTTPInternalServerError def new_subscription(self, request): """ Generate a new subscription ID for the user's queue. """ - import pdb; pdb.set_trace(); - username = request.environ['REMOTE_USER'] + #TODO add auth here too and all other REMOTE_USER instances. self._init(self.app.config) + username = self._get_uid(request) + self.auth.authenticate_user(username, + request.params.get('password')); try: logger.debug("New subscription request: '%s'", request.body) @@ -103,7 +118,7 @@ def new_subscription(self, request): def remove_subscription(self, request): """ Remove a subscription from a user's queue. """ - username = request.environ['REMOTE_USER'] + username = self._get_uid(request) self._init(self.app.config) try: @@ -127,11 +142,11 @@ def remove_subscription(self, request): except NotifStorageException, e: return HTTPBadRequest(e.args[0]) except Exception, e: - logger.error("Error deleting subscription, %s", str(e)) + logger.error("Error deleting subscription, %s" % str(e)) raise HTTPInternalServerError() def broadcast(self, request): - username = request.environ['REMOTE_USER'] + username = self._get_uid(request) self._init(self.app.config) try: @@ -139,7 +154,7 @@ def broadcast(self, request): logger.debug("Broadcast Message length: %s", len(request.body)) broadcast_msg = json.loads(request.body) except ValueError, verr: - logger.error("Error parsing broadcast JSON %s", str(verr)) + logger.error("Error parsing broadcast JSON %s" % str(verr)) raise HTTPBadRequest("Invalid JSON") if 'body' not in broadcast_msg: @@ -150,13 +165,13 @@ def broadcast(self, request): logger.error("HMAC not specified") raise HTTPBadRequest("Need to include HMAC with broadcast") - logger.info("Broadcasting message for user '%s'", username) + logger.info("Broadcasting message for user '%s'" % username) try: self.msg_backend.send_broadcast(request.body, username) return HTTPOk() except: - logger.error("Error sending broadcast message to user '%s'", + logger.error("Error sending broadcast message to user '%s'" % username) raise HTTPInternalServerError() diff --git a/notifserver/storage/mongo.py b/notifserver/storage/mongo.py index 7c42bd6..5d6025d 100644 --- a/notifserver/storage/mongo.py +++ b/notifserver/storage/mongo.py @@ -88,9 +88,9 @@ def create_client_queue(self, username): u'created': int(time.time()) } - logger.info("Creating incoming queue %s for user %s", + logger.info("Creating incoming queue %s for user %s" % ( channel_info.get('token'), - username) + username)) try: self.db.user.insert(channel_info, safe=True) return {'queue_id': channel_info['token'], diff --git a/notifserver/storage/redis_file.py b/notifserver/storage/redis_file.py index ef9bdaa..dc3e58f 100644 --- a/notifserver/storage/redis_file.py +++ b/notifserver/storage/redis_file.py @@ -86,7 +86,7 @@ def new_token(self): return "%x" % random.getrandbits(256) def create_client_queue(self, username): - logger.info("Creating incoming queue for %s for user %s") + logger.info("Creating incoming queue for user %s" % username) try: # Check to see if the user already has a token. # ut: user -> token diff --git a/notifserver/wsgiapp.py b/notifserver/wsgiapp.py index 040bd2e..45fbda7 100644 --- a/notifserver/wsgiapp.py +++ b/notifserver/wsgiapp.py @@ -74,7 +74,6 @@ class NotificationServerApp(SyncServerApp): def __init__(self, urls, controllers, config, auth_class): """ Main storage """ - import pdb; pdb.set_trace(); super(NotificationServerApp, self).__init__(urls = urls, controllers = controllers, config = config, diff --git a/setup.py b/setup.py index fa4d25e..eaf90c4 100644 --- a/setup.py +++ b/setup.py @@ -62,10 +62,17 @@ 'WebTest', ] +try: + with open('README.txt') as file: + long_desc = file.read +except: + long_desc = "TBD" + + setup(name='NotifServer', author='Mozilla Services Group', url='http://hg.mozilla.org/services/', description='Mozilla Push Notification Server', - long_description=open('README.txt').read(), + long_description=long_desc, author_email='dev_services@mozilla.com', version=0.1, packages=find_packages(), entry_points=entry_points, install_requires=requires, diff --git a/start.sh b/start.sh index af9df57..9d845fa 100755 --- a/start.sh +++ b/start.sh @@ -1 +1,2 @@ -bin/gunicorn -w1 notifserver.run -t 3000 --log-file - --log-level info +#bin/gunicorn -w1 notifserver.run -t 3000 --log-file - --log-level info +bin/paster serve etc/notifserver/production.ini