RootFactory.__init__()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.125

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 2
ccs 1
cts 2
cp 0.5
crap 1.125
rs 10
1
# Copyright 2013 Netherlands eScience Center
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#   http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14
15 1
import os
16 1
import datetime
17 1
import decimal
18 1
import logging
19 1
import psycopg2
20 1
import simplejson
21 1
from psycopg2.extras import RealDictCursor
22 1
from pyramid.authentication import RemoteUserAuthenticationPolicy
23 1
from pyramid.authorization import ACLAuthorizationPolicy
24 1
from pyramid.config import Configurator
25 1
from pyramid.events import NewRequest
26 1
from pyramid.events import subscriber
27 1
from pyramid.security import Allow, Authenticated, ALL_PERMISSIONS, DENY_ALL
28 1
from pyramid.security import unauthenticated_userid
29 1
from pyramid.renderers import JSON
30 1
from .version import __version__
31
32 1
logger = logging.getLogger(__package__)
33
34
35 1
def dbsession(dsn):
36
    return psycopg2.connect(dsn, cursor_factory=RealDictCursor)
37
38 1
def request_credentials(request):
39
    """Returns the username/password from the authorization header with Basic Authentication.
40
41
    When authorization header is missing it returns (None, None) tuple.
42
    """
43
    if 'HTTP_AUTHORIZATION' not in request.environ:
44
        logger.warn('No HTTP_AUTHORIZATION found, using empty credentials')
45
        return (None, None)
46
    (method, auth) = request.environ['HTTP_AUTHORIZATION'].split(' ', 1)
47
    if method.lower() != 'basic':
48
        err = 'Can only request credentials from Basic Authentication'
49
        raise NotImplementedError(err)
50
    (username, password) = auth.strip().decode('base64').split(':', 1)
51
    return (username, password)
52
53 1
def _connect(request):
54
    settings = request.registry.settings
55
    (username, password) = request_credentials(request)
56
    dsn = settings['dsn'].format(username=username, password=password, host=os.environ.get('DB_HOST', ''))
57
    conn = dbsession(dsn)
58
59
    def cleanup(_):
60
        conn.close()
61
62
    request.add_finished_callback(cleanup)
63
    return conn
64
65
66 1
@subscriber(NewRequest)
67
def new_request(event):
68
    """Make db connection available as request.db"""
69
    request = event.request
70
    request.set_property(_connect, 'db', reify=True)
71
72
73 1
def get_user(request):
74
    return unauthenticated_userid(request)
75
76
77 1
class RootFactory(object):
78 1
    __acl__ = [(Allow, Authenticated, 'view'), DENY_ALL]
79
80 1
    def __init__(self, request):
81
        pass
82
83
84 1
def datetime_adaptor(obj, request):
85 1
    return obj.isoformat()
86 1
def timedelta_adaptor(obj, request):
87 1
    return str(obj)
88 1
def decimal_adaptor(obj, request):
89 1
    return float(obj)
90 1
def cursor_adaptor(obj, request):
91
    # would like to use yield, but json lib doesnt do iterators so unroll cursor into list
92 1
    return list(obj)
93
94 1
def main(global_config, **settings):
95
    """ This function returns a Pyramid WSGI application. """
96
    config = Configurator(settings=settings)
97
    config.add_request_method(get_user, 'user', reify=True)
98
    authen = RemoteUserAuthenticationPolicy('HTTP_X_PROXY_REMOTE_USER')
99
    config.set_authentication_policy(authen)
100
    config.set_default_permission('view')
101
    config.set_authorization_policy(ACLAuthorizationPolicy())
102
    config.set_root_factory(RootFactory)
103
104
    # The default json renderer is pure python try other implementations
105
    # Benchmarks with 760/2013-05-31T00:00:00Z/2013-08-09T00:00:00Z
106
    # json = 41s
107
    # simplejson = 26s
108
    # omnijson = ~ incorrect response no adaptor support
109
    # ujson = ~ incorrect response no adaptor support
110
    # yajl = ~ incorrect response no adaptor support
111
    # jsonlib2 = 58s
112
    # jsonlib =  61s
113
    # anyjson = ~ incorrect response no adaptor support
114
    # Conclusion use simplejson
115
    json_renderer = JSON(serializer=simplejson.dumps)
116
    json_renderer.add_adapter(datetime.datetime, datetime_adaptor)
117
    json_renderer.add_adapter(datetime.timedelta, timedelta_adaptor)
118
    json_renderer.add_adapter(decimal.Decimal, decimal_adaptor)
119
    json_renderer.add_adapter(RealDictCursor, cursor_adaptor)
120
    config.add_renderer('json', json_renderer)
121
122
    config.add_static_view('/static', 'annotation:static')
123
    config.add_route('home', '/')
124
    config.add_route('trackers', '/trackers')
125
    config.add_route('tracker', '/tracker/{id}/{start}/{end}')
126
    config.add_route('uploads.html', '/uploads.html')
127
    config.add_route('annotations.html', '/uploads/{table}/annotations.html')
128
    config.add_route('annotations.csv', '/uploads/{table}/annotations.csv')
129
    config.scan()
130
    return config.make_wsgi_app()
131