~ken-vandine/desktopcouch/0_4_1

« back to all changes in this revision

Viewing changes to desktopcouch/records/server_base.py

  • Committer: Tarmac
  • Author(s): Chad Miller
  • Date: 2009-09-14 21:40:54 UTC
  • mfrom: (61.1.13 trunk-0.4)
  • Revision ID: bzrdev@chad.org-20090914214054-pr2kuxm0hhyi4pzw
Add OAuth requirement to desktopcouch and require authentication.

Improve replication peer-to-peer and add replication to Ubuntu One service.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
"""The Desktop Couch Records API."""
23
23
 
24
24
from couchdb import Server
25
 
from couchdb.client import ResourceNotFound, ResourceConflict
 
25
from couchdb.client import ResourceNotFound, ResourceConflict, Resource
26
26
from couchdb.design import ViewDefinition
27
 
import desktopcouch
28
27
from record import Record
29
 
 
 
28
import httplib2
 
29
from oauth import oauth
 
30
import urlparse
 
31
import cgi
30
32
 
31
33
#DEFAULT_DESIGN_DOCUMENT = "design"
32
34
DEFAULT_DESIGN_DOCUMENT = None  # each view in its own eponymous design doc.
43
45
        return ("Database %s does not exist on this server. (Create it by "
44
46
                "passing create=True)") % self.database
45
47
 
 
48
class OAuthAuthentication(httplib2.Authentication):
 
49
    """An httplib2.Authentication subclass for OAuth"""
 
50
    def __init__(self, oauth_data, host, request_uri, headers, response, 
 
51
        content, http):
 
52
        self.oauth_data = oauth_data
 
53
        httplib2.Authentication.__init__(self, None, host, request_uri, 
 
54
              headers, response, content, http)
 
55
 
 
56
    def request(self, method, request_uri, headers, content):
 
57
        """Modify the request headers to add the appropriate
 
58
        Authorization header."""
 
59
        consumer = oauth.OAuthConsumer(self.oauth_data['consumer_key'], 
 
60
           self.oauth_data['consumer_secret'])
 
61
        access_token = oauth.OAuthToken(self.oauth_data['token'], 
 
62
           self.oauth_data['token_secret'])
 
63
        full_http_url = "http://%s%s" % (self.host, request_uri)
 
64
        schema, netloc, path, params, query, fragment = urlparse.urlparse(full_http_url)
 
65
        querystr_as_dict = dict(cgi.parse_qsl(query))
 
66
        req = oauth.OAuthRequest.from_consumer_and_token(
 
67
            consumer,
 
68
            access_token,
 
69
            http_method = method,
 
70
            http_url = full_http_url,
 
71
            parameters = querystr_as_dict
 
72
        )
 
73
        req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, access_token)
 
74
        headers.update(httplib2._normalize_headers(req.to_header()))
 
75
 
 
76
class OAuthCapableHttp(httplib2.Http):
 
77
    """Subclass of httplib2.Http which specifically uses our OAuth 
 
78
       Authentication subclass (because httplib2 doesn't know about it)"""
 
79
    def add_oauth_tokens(self, consumer_key, consumer_secret, 
 
80
                         token, token_secret):
 
81
        self.oauth_data = {
 
82
            "consumer_key": consumer_key,
 
83
            "consumer_secret": consumer_secret,
 
84
            "token": token,
 
85
            "token_secret": token_secret
 
86
        }
 
87
 
 
88
    def _auth_from_challenge(self, host, request_uri, headers, response, content):
 
89
        """Since we know we're talking to desktopcouch, and we know that it
 
90
           requires OAuth, just return the OAuthAuthentication here rather
 
91
           than checking to see which supported auth method is required."""
 
92
        yield OAuthAuthentication(self.oauth_data, host, request_uri, headers, 
 
93
                response, content, self)
46
94
 
47
95
def row_is_deleted(row):
48
96
    """Test if a row is marked as deleted.  Smart views 'maps' should not
55
103
        return False
56
104
 
57
105
 
58
 
class CouchDatabase(object):
 
106
class CouchDatabaseBase(object):
59
107
    """An small records specific abstraction over a couch db database."""
60
108
 
61
 
    def __init__(self, database, uri=None, record_factory=None, create=False,
 
109
    def __init__(self, database, uri, record_factory=None, create=False,
62
110
                 server_class=Server):
63
 
        if not uri:
64
 
            desktopcouch.find_pid()
65
 
            port = desktopcouch.find_port()
66
 
            self.server_uri = "http://localhost:%s" % port
67
 
        else:
68
 
            self.server_uri = uri
 
111
        self.server_uri = uri
69
112
        self._server = server_class(self.server_uri)
70
113
        if database not in self._server:
71
114
            if create:
224
267
            return []
225
268
 
226
269
    def get_records(self, record_type=None, create_view=False,
227
 
            design_doc=DEFAULT_DESIGN_DOCUMENT, version="1"):
 
270
            design_doc=DEFAULT_DESIGN_DOCUMENT):
228
271
        """A convenience function to get records from a view named
229
 
        C{get_records_and_type}, suffixed with C{__v} and the supplied version
230
 
        string (or default of "1").  We optionally create a view in the design
 
272
        C{get_records_and_type}.  We optionally create a view in the design
231
273
        document.  C{create_view} may be True or False, and a special value,
232
274
        None, is analogous to  O_EXCL|O_CREAT .
233
275
 
262
304
        if design_doc is None:
263
305
            design_doc = view_name
264
306
 
265
 
        if not version is None:  # versions do not affect design_doc name.
266
 
            view_name = view_name + "__v" + version
267
 
 
268
307
        exists = self.view_exists(view_name, design_doc)
269
308
 
270
309
        if exists: