Switch to unified view

a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
1
# -*- coding: utf-8 -*-
1
# -*- coding: utf-8 -*-
2
"""REST Controller"""
2
"""REST Controller"""
3
import logging
3
import logging
4
4
5
import oauth2 as oauth
5
from webob import exc
6
from webob import exc
6
from tg import expose
7
from tg import expose, flash, redirect
7
from pylons import c, request
8
from pylons import c, request
9
10
from ming.orm import session
11
from ming.utils import LazyProperty
8
12
9
from allura import model as M
13
from allura import model as M
10
from allura.lib import helpers as h
14
from allura.lib import helpers as h
15
from allura.lib import security 
11
16
12
log = logging.getLogger(__name__)
17
log = logging.getLogger(__name__)
13
action_logger = h.log_action(log, 'API:')
18
action_logger = h.log_action(log, 'API:')
14
19
15
class RestController(object):
20
class RestController(object):
16
21
22
    def __init__(self):
23
        self.oauth = OAuthNegotiator()
24
17
    def _authenticate_request(self):
25
    def _authenticate_request(self):
18
        'Based on request.params, authenticate the request'
26
        'Based on request.params or oauth, authenticate the request'
27
        if 'oauth_token' in request.params:
28
            return self.oauth._authenticate()
19
        if 'api_key' not in request.params:
29
        if 'api_key' not in request.params:
20
            return None
30
            return None
21
        api_key = request.params.get('api_key')
31
        api_key = request.params.get('api_key')
22
        api_token = M.ApiToken.query.get(api_key=api_key)
32
        api_token = M.ApiToken.query.get(api_key=api_key)
23
        if api_token is not None and api_token.authenticate_request(request.path, request.params):
33
        if api_token is not None and api_token.authenticate_request(request.path, request.params):
...
...
35
            c.user = M.User.anonymous()
45
            c.user = M.User.anonymous()
36
        neighborhood = M.Neighborhood.query.get(url_prefix = '/' + name + '/')
46
        neighborhood = M.Neighborhood.query.get(url_prefix = '/' + name + '/')
37
        if not neighborhood: raise exc.HTTPNotFound, name
47
        if not neighborhood: raise exc.HTTPNotFound, name
38
        return NeighborhoodRestController(neighborhood), remainder
48
        return NeighborhoodRestController(neighborhood), remainder
39
49
50
class OAuthNegotiator(object):
51
52
    @LazyProperty
53
    def server(self):
54
        result = oauth.Server()
55
        result.add_signature_method(oauth.SignatureMethod_PLAINTEXT())
56
        result.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())
57
        return result
58
59
    def _authenticate(self):
60
        req = oauth.Request.from_request(
61
            request.method,
62
            request.url.split('?')[0],
63
            headers=request.headers,
64
            parameters=dict(request.params),
65
            query_string=request.query_string
66
            )
67
        consumer_token = M.OAuthConsumerToken.query.get(
68
            api_key=req['oauth_consumer_key'])
69
        access_token = M.OAuthAccessToken.query.get(
70
            api_key=req['oauth_token'])
71
        if consumer_token is None:
72
            log.error('Invalid consumer token')
73
            return None
74
            raise exc.HTTPForbidden
75
        if access_token is None:
76
            log.error('Invalid access token')
77
            raise exc.HTTPForbidden
78
        consumer = consumer_token.consumer
79
        try:
80
            self.server.verify_request(req, consumer, access_token.as_token())
81
        except:
82
            log.error('Invalid signature')
83
            raise exc.HTTPForbidden 
84
        return access_token
85
86
    @expose()
87
    def request_token(self, **kw):
88
        req = oauth.Request.from_request(
89
            request.method,
90
            request.url.split('?')[0],
91
            headers=request.headers,
92
            parameters=dict(request.params),
93
            query_string=request.query_string
94
            )
95
        consumer_token = M.OAuthConsumerToken.query.get(
96
            api_key=req['oauth_consumer_key'])
97
        if consumer_token is None:
98
            log.error('Invalid consumer token')
99
            raise exc.HTTPForbidden
100
        consumer = consumer_token.consumer
101
        try:
102
            self.server.verify_request(req, consumer, None)
103
        except:
104
            log.error('Invalid signature')
105
            raise exc.HTTPForbidden
106
        req_token = M.OAuthRequestToken(
107
            consumer_token_id=consumer_token._id,
108
            callback=req.get('oauth_callback', 'oob')
109
            )
110
        session(req_token).flush()
111
        log.info('Saving new request token with key: %s', req_token.api_key)
112
        return req_token.to_string()
113
114
    @expose('jinja:oauth_authorize.html')
115
    def authorize(self, oauth_token=None):
116
        security.require_authenticated()
117
        rtok = M.OAuthRequestToken.query.get(api_key=oauth_token)
118
        rtok.user_id = c.user._id
119
        if rtok is None:
120
            log.error('Invalid token %s', oauth_token)
121
            raise exc.HTTPForbidden
122
        return dict(
123
            oauth_token=oauth_token,
124
            consumer=rtok.consumer_token)
125
        
126
    @expose('jinja:oauth_authorize_ok.html')
127
    def do_authorize(self, yes=None, no=None, oauth_token=None):
128
        security.require_authenticated()
129
        rtok = M.OAuthRequestToken.query.get(api_key=oauth_token)
130
        if no:
131
            rtok.delete()
132
            flash('%s NOT AUTHORIZED' % rtok.consumer_token.name, 'error')
133
            redirect('/auth/oauth/')
134
        if rtok.callback == 'oob':
135
            rtok.validation_pin = h.nonce(6)
136
            return dict(rtok=rtok)
137
        rtok.validation_pin = h.nonce(20)
138
        if '?' in rtok.callback:
139
            url = rtok.callback + '&'
140
        else:
141
            url = rtok.callback + '?'
142
        url+='oauth_token=%s&oauth_verifier=%s' % (
143
            rtok.api_key, rtok.validation_pin)
144
        redirect(url)
145
        
146
    @expose()
147
    def access_token(self, **kw):
148
        req = oauth.Request.from_request(
149
            request.method,
150
            request.url.split('?')[0],
151
            headers=request.headers,
152
            parameters=dict(request.params),
153
            query_string=request.query_string
154
            )
155
        consumer_token = M.OAuthConsumerToken.query.get(
156
            api_key=req['oauth_consumer_key'])
157
        request_token = M.OAuthRequestToken.query.get(
158
            api_key=req['oauth_token'])
159
        if consumer_token is None:
160
            log.error('Invalid consumer token')
161
            raise exc.HTTPForbidden
162
        if request_token is None:
163
            log.error('Invalid request token')
164
            raise exc.HTTPForbidden
165
        pin = req['oauth_verifier']
166
        if pin != request_token.validation_pin:
167
            log.error('Invalid verifier')
168
            raise exc.HTTPForbidden
169
        rtok = request_token.as_token()
170
        rtok.set_verifier(pin)
171
        consumer = consumer_token.consumer
172
        try:
173
            self.server.verify_request(req, consumer, rtok)
174
        except:
175
            log.error('Invalid signature')
176
            return None
177
        acc_token = M.OAuthAccessToken(
178
            consumer_token_id=consumer_token._id,
179
            request_token_id=request_token._id,
180
            user_id=request_token.user_id)
181
        return acc_token.to_string()
182
40
class NeighborhoodRestController(object):
183
class NeighborhoodRestController(object):
41
184
42
    def __init__(self, neighborhood):
185
    def __init__(self, neighborhood):
43
        self._neighborhood = neighborhood
186
        self._neighborhood = neighborhood
44
187