Skip to content

Commit

Permalink
Added initial browserID support
Browse files Browse the repository at this point in the history
TODO:
* Fix browserID to to full local verification (pull public key from remote
sites)
* a bit more direct regarding auth routines in clientagent.py
* work arounds for some framework issues.
  • Loading branch information
jrconlin committed Sep 29, 2011
1 parent 402bdaa commit ded4bb7
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 27 deletions.
1 change: 0 additions & 1 deletion README.txt
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# TBD
60 changes: 60 additions & 0 deletions etc/notifserver/development.ini
Original file line number Diff line number Diff line change
@@ -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


14 changes: 12 additions & 2 deletions notifserver/auth/browserid.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
15 changes: 10 additions & 5 deletions notifserver/auth/jws.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
from M2Crypto import RSA, BIO, ASN1
from hashlib import sha256, sha384, sha512


import base64
import cef
import hmac
Expand Down Expand Up @@ -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"))
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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')}
Expand Down Expand Up @@ -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 = {
Expand Down
41 changes: 28 additions & 13 deletions notifserver/controllers/clientagent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -127,19 +142,19 @@ 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:
logger.debug("Broadcast request: '%s'", request.body)
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:
Expand All @@ -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()

Expand Down
4 changes: 2 additions & 2 deletions notifserver/storage/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
2 changes: 1 addition & 1 deletion notifserver/storage/redis_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion notifserver/wsgiapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 8 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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='[email protected]',
version=0.1, packages=find_packages(),
entry_points=entry_points, install_requires=requires,
Expand Down
3 changes: 2 additions & 1 deletion start.sh
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ded4bb7

Please sign in to comment.