Merge lp://qastaging/~thisfred/desktopcouch/noauth into lp://qastaging/desktopcouch

Proposed by Eric Casteleijn
Status: Merged
Approved by: Elliot Murphy
Approved revision: 48
Merged at revision: not available
Proposed branch: lp://qastaging/~thisfred/desktopcouch/noauth
Merge into: lp://qastaging/desktopcouch
Diff against target: None lines
To merge this branch: bzr merge lp://qastaging/~thisfred/desktopcouch/noauth
Reviewer Review Type Date Requested Status
Elliot Murphy (community) Approve
Review via email: mp+10668@code.qastaging.launchpad.net

Commit message

This comments out two sections in the .ini file, to disable authentication, since it's not working as long as couchdb-478 is not fixed.

This has these four other branches merged in, and all conflicts removed:

lp:~thisfred/desktopcouch/any_port_in_a_storm/
lp:~sil/desktopcouch/create-oauth-tokens-startup
lp:~sil/desktopcouch/get-oauth-tokens-function
lp:~sil/desktopcouch/not-so-random

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

This comments out two sections in the .ini file, to disable authentication, since it's not working as long as couchdb-478 is not fixed.

This has these four other branches merged in, and all conflicts removed:

lp:~thisfred/desktopcouch/any_port_in_a_storm/
lp:~sil/desktopcouch/create-oauth-tokens-startup
lp:~sil/desktopcouch/get-oauth-tokens-function
lp:~sil/desktopcouch/not-so-random

Revision history for this message
Elliot Murphy (statik) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/couchdb.tmpl'
2--- data/couchdb.tmpl 2009-07-14 13:53:21 +0000
3+++ data/couchdb.tmpl 2009-08-20 12:55:54 +0000
4@@ -31,7 +31,7 @@
5 come back to browse your CouchDB again.</p>
6 <p>Don't bookmark the CouchDB page itself, because its location may change!</p>
7 <p>Taking you to your Desktop CouchDB in <span>30</span> seconds...
8-<a id="there" href="http://localhost:[[COUCHDB_PORT]]/_utils">take me
9+<a id="there" href="http://[[COUCHDB_USERNAME]]:[[COUCHDB_PASSWORD]]@localhost:[[COUCHDB_PORT]]/_utils">take me
10 there straight away from now on</a> (remember to bookmark this page first!)</p>
11 </body>
12 </html>
13
14=== modified file 'desktopcouch/__init__.py'
15--- desktopcouch/__init__.py 2009-08-21 21:57:37 +0000
16+++ desktopcouch/__init__.py 2009-08-24 21:05:33 +0000
17@@ -16,7 +16,7 @@
18 "Desktop Couch helper files"
19
20 from __future__ import with_statement
21-import os, re, time
22+import os, time
23
24
25 def find_pid(start_if_not_running=True):
26@@ -80,60 +80,22 @@
27
28 return True
29
30-
31-def find_port__linux(pid):
32- # Look in the CouchDB log to find the port number, someday.
33- # Currently, we have to grovel around in /proc instead.
34- # Oh, the huge manatee... (this replaced an lsof shell recipe
35- # which was shorter but less reliable)
36-
37- proc_dir = "/proc/%s" % (pid,)
38-
39- # enumerate the process' file descriptors
40- fd_dir = os.path.join(proc_dir, 'fd')
41- try:
42- fd_paths = [os.readlink(os.path.join(fd_dir, fd))
43- for fd in os.listdir(fd_dir)]
44- except OSError:
45- raise RuntimeError("Unable to find file descriptors in /proc")
46-
47-
48- # identify socket fds
49- socket_matches = [re.match('socket:\\[([0-9]+)\\]', p) for p in fd_paths]
50- # extract their inode numbers
51- socket_inodes = [m.group(1) for m in socket_matches if m is not None]
52-
53- # construct a subexpression which matches any one of these inodes
54- inode_subexp = "|".join(socket_inodes)
55- # construct regexp to match /proc/net/tcp entries which are listening
56- # sockets having one of the given inode numbers
57- listening_regexp = re.compile(r'''
58- \s*\d+:\s* # sl
59- 0100007F:([0-9A-F]{4})\s+ # local_address
60- 00000000:0000\s+ # rem_address
61- 0A\s+ # st (0A = listening)
62- [0-9A-F]{8}: # tx_queue
63- [0-9A-F]{8}\s+ # rx_queue
64- [0-9A-F]{2}: # tr
65- [0-9A-F]{8}\s+ # tm->when
66- [0-9A-F]{8}\s* # retrnsmt
67- \d+\s+\d+\s+ # uid, timeout
68- (?:%s)\s+ # inode
69- ''' % (inode_subexp,), re.VERBOSE)
70-
71- # extract the TCP port from the first matching line in /proc/$pid/net/tcp
72- port = None
73- with open(os.path.join(proc_dir, 'net', 'tcp')) as tcp_file:
74- for line in tcp_file:
75- match = listening_regexp.match(line)
76- if match is not None:
77- port = str(int(match.group(1), 16))
78- break
79- if port is None:
80- raise RuntimeError("Unable to find listening port")
81-
82- return port
83-
84+def find_port():
85+ """Look in the CouchDB log to find the port number."""
86+
87+ from desktopcouch.local_files import FILE_LOG
88+ with open(FILE_LOG, 'r') as log_file:
89+ port = None
90+ try:
91+ last_line = [
92+ line for line in log_file if
93+ 'Apache CouchDB has started on http://' in line][-1]
94+ except IndexError:
95+ return None
96+ port = last_line.split(':')[-1].strip()
97+ if port.endswith('/'):
98+ port = port[:-1]
99+ return int(port)
100
101
102 import platform
103@@ -142,9 +104,5 @@
104 process_is_couchdb = {
105 "Linux": process_is_couchdb__linux
106 } [os_name]
107-
108- find_port = {
109- "Linux": find_port__linux
110- } [os_name]
111 except KeyError:
112 raise NotImplementedError("os %r is not yet supported" % (os_name,))
113
114=== modified file 'desktopcouch/contacts/contactspicker.py'
115--- desktopcouch/contacts/contactspicker.py 2009-08-22 17:22:02 +0000
116+++ desktopcouch/contacts/contactspicker.py 2009-08-24 20:35:25 +0000
117@@ -19,17 +19,15 @@
118 """A widget to allow users to pick contacts"""
119
120 import gtk
121-from desktopcouch.contacts.record import (
122- Contact,
123- CONTACT_RECORD_TYPE)
124+from desktopcouch.contacts.record import CONTACT_RECORD_TYPE
125 from desktopcouch.records.couchgrid import CouchGrid
126
127+
128 class ContactsPicker(gtk.VBox):
129+ """A contacts picker"""
130
131- def __init__(self):
132- """Create a new ContactsPicker widget
133-
134- """
135+ def __init__(self, uri=None):
136+ """Create a new ContactsPicker widget."""
137
138 gtk.VBox.__init__(self)
139
140@@ -40,11 +38,12 @@
141 self.search_entry = gtk.Entry()
142 hbox.pack_start(self.search_entry, True, True, 3)
143
144- self.search_button = gtk.Button(stock=gtk.STOCK_FIND, use_underline=True)
145+ self.search_button = gtk.Button(
146+ stock=gtk.STOCK_FIND, use_underline=True)
147 hbox.pack_start(self.search_button, False, False, 3)
148
149 # Create CouchGrid to contain list of contacts
150- self.contacts_list = CouchGrid('contacts')
151+ self.contacts_list = CouchGrid('contacts', uri=uri)
152 self.contacts_list.editable = False
153 self.contacts_list.keys = [ "first_name", "last_name" ]
154 self.contacts_list.record_type = CONTACT_RECORD_TYPE
155
156=== modified file 'desktopcouch/contacts/tests/test_contactspicker.py'
157--- desktopcouch/contacts/tests/test_contactspicker.py 2009-08-22 17:22:02 +0000
158+++ desktopcouch/contacts/tests/test_contactspicker.py 2009-08-24 20:35:25 +0000
159@@ -21,10 +21,24 @@
160 import testtools
161 import gtk
162 from desktopcouch.contacts.contactspicker import ContactsPicker
163+from desktopcouch.records.server import CouchDatabase
164+from desktopcouch.records.tests import get_uri
165+
166+URI = get_uri()
167
168 class TestContactsPicker(testtools.TestCase):
169 """Test the Contact Picker Window."""
170
171+ def setUp(self):
172+ """setup each test"""
173+ # Connect to CouchDB server
174+ self.dbname = 'contacts'
175+ self.database = CouchDatabase(self.dbname, create=True, uri=URI)
176+
177+ def tearDown(self):
178+ """tear down each test"""
179+ del self.database._server[self.dbname]
180+
181 def test_can_contruct_contactspicker(self):
182 """Test that we can build the window"""
183 # Create and show a test window
184@@ -34,8 +48,8 @@
185 win.resize(300, 450)
186
187 # Create the contacts picker widget
188- picker = ContactsPicker()
189+ picker = ContactsPicker(uri=URI)
190 win.get_content_area().pack_start(picker, True, True, 3)
191- self.assert_(picker.get_contacts_list())
192+ self.assert_(picker.get_contacts_list())
193
194
195
196=== modified file 'desktopcouch/local_files.py'
197--- desktopcouch/local_files.py 2009-08-21 22:45:24 +0000
198+++ desktopcouch/local_files.py 2009-08-25 16:01:15 +0000
199@@ -64,6 +64,43 @@
200
201 return chain
202
203+class NoOAuthTokenException(Exception):
204+ def __str__(self):
205+ return "OAuth details were not found in the ini file (%s)" % FILE_INI
206+
207+def get_oauth_tokens():
208+ """Return the OAuth tokens from the desktop Couch ini file.
209+ CouchDB OAuth is two-legged OAuth (not three-legged like most OAuth).
210+ We have one "consumer", defined by a consumer_key and a secret,
211+ and an "access token", defined by a token and a secret.
212+ The OAuth signature is created with reference to these and the requested
213+ URL.
214+ (More traditional 3-legged OAuth starts with a "request token" which is
215+ then used to procure an "access token". We do not require this.)
216+ """
217+ import ConfigParser
218+ c = ConfigParser.ConfigParser()
219+ # monkeypatch ConfigParser to stop it lower-casing option names
220+ c.optionxform = lambda s: s
221+ c.read(FILE_INI)
222+ try:
223+ oauth_token_secrets = c.items("oauth_token_secrets")
224+ oauth_consumer_secrets = c.items("oauth_consumer_secrets")
225+ except ConfigParser.NoSectionError:
226+ raise NoOAuthTokenException
227+ if not oauth_token_secrets or not oauth_consumer_secrets:
228+ raise NoOAuthTokenException
229+ try:
230+ out = {
231+ "token": oauth_token_secrets[0][0],
232+ "token_secret": oauth_token_secrets[0][1],
233+ "consumer_key": oauth_consumer_secrets[0][0],
234+ "consumer_secret": oauth_consumer_secrets[0][1]
235+ }
236+ except IndexError:
237+ raise NoOAuthTokenException
238+ return out
239+
240 # You will need to add -b or -k on the end of this
241 COUCH_EXEC_COMMAND = [COUCH_EXE, couch_chain_flag(), FILE_INI, '-p', FILE_PID,
242 '-o', FILE_STDOUT, '-e', FILE_STDERR]
243
244=== modified file 'desktopcouch/records/server.py'
245--- desktopcouch/records/server.py 2009-08-21 22:45:24 +0000
246+++ desktopcouch/records/server.py 2009-08-24 20:35:25 +0000
247@@ -49,8 +49,8 @@
248
249 def __init__(self, database, uri=None, record_factory=None, create=False):
250 if not uri:
251- pid = desktopcouch.find_pid()
252- port = desktopcouch.find_port(pid)
253+ desktopcouch.find_pid()
254+ port = desktopcouch.find_port()
255 self.server_uri = "http://localhost:%s" % port
256 else:
257 self.server_uri = uri
258
259=== modified file 'desktopcouch/records/tests/__init__.py'
260--- desktopcouch/records/tests/__init__.py 2009-08-21 22:47:25 +0000
261+++ desktopcouch/records/tests/__init__.py 2009-08-24 20:35:25 +0000
262@@ -15,26 +15,36 @@
263 # along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
264 """Tests for Documents API"""
265
266-import os, tempfile
267-from desktopcouch.local_files import COUCH_EXE, couch_chain_flag
268+import os, tempfile, time, urllib2
269+from desktopcouch import local_files
270 from desktopcouch.start_local_couchdb import create_ini_file, run_couchdb
271
272 directory = tempfile.mkdtemp()
273-ini_path = os.path.join(directory, "test_couchdb.ini")
274-log_path = os.path.join(directory, "test_couchdb.log")
275-pid_path = os.path.join(directory, "test_couchdb.pid")
276-stdout_path = os.path.join(directory, "test_couchdb.stdout")
277-stderr_path = os.path.join(directory, "test_couchdb.stderr")
278-
279-db_dir = os.path.join(directory, "test_couchdb")
280-os.mkdir(db_dir)
281-
282-port = "21224"
283-create_ini_file(
284- ini_path=ini_path, db_dir=db_dir, log_path=log_path, port=port)
285-exec_command = [
286- COUCH_EXE, couch_chain_flag(), ini_path, '-p', pid_path, '-o', stdout_path,
287- '-e', stderr_path]
288-exec_command[2] = ini_path
289-run_couchdb(exec_command=exec_command)
290-URI = "htpp://localhost:%s" % port
291+local_files.FILE_INI = os.path.join(directory, "test_couchdb.ini")
292+local_files.FILE_LOG = os.path.join(directory, "test_couchdb.log")
293+local_files.FILE_PID = os.path.join(directory, "test_couchdb.pid")
294+local_files.FILE_STDOUT = os.path.join(directory, "test_couchdb.stdout")
295+local_files.FILE_STDERR = os.path.join(directory, "test_couchdb.stderr")
296+
297+local_files.DIR_DB = os.path.join(directory, "test_couchdb")
298+os.mkdir(local_files.DIR_DB)
299+
300+PORT = "21224"
301+create_ini_file(port=PORT)
302+local_files.COUCH_EXEC_COMMAND = [
303+ local_files.COUCH_EXE, local_files.couch_chain_flag(), local_files.FILE_INI,
304+ '-p', local_files.FILE_PID, '-o', local_files.FILE_STDOUT, '-e',
305+ local_files.FILE_STDERR]
306+run_couchdb()
307+
308+def get_uri():
309+ uri = 'http://localhost:%s' % PORT
310+ i = 0
311+ while i < 100:
312+ try:
313+ urllib2.urlopen(uri)
314+ break
315+ except:
316+ time.sleep(0.1)
317+ i += 1
318+ return uri
319
320=== modified file 'desktopcouch/records/tests/test_couchgrid.py'
321--- desktopcouch/records/tests/test_couchgrid.py 2009-08-22 17:22:02 +0000
322+++ desktopcouch/records/tests/test_couchgrid.py 2009-08-24 20:35:25 +0000
323@@ -22,8 +22,9 @@
324 from desktopcouch.records.record import Record
325 from desktopcouch.records.server import CouchDatabase
326 from desktopcouch.records.couchgrid import CouchGrid
327-from desktopcouch.records.tests import URI
328+from desktopcouch.records.tests import get_uri
329
330+URI = get_uri()
331
332 class TestCouchGrid(TestCase):
333 """Test the CouchGrid functionality"""
334
335=== modified file 'desktopcouch/records/tests/test_server.py'
336--- desktopcouch/records/tests/test_server.py 2009-08-21 22:45:24 +0000
337+++ desktopcouch/records/tests/test_server.py 2009-08-24 20:35:25 +0000
338@@ -20,7 +20,9 @@
339 import testtools
340 from desktopcouch.records.server import CouchDatabase
341 from desktopcouch.records.record import Record
342-from desktopcouch.records.tests import URI
343+from desktopcouch.records.tests import get_uri
344+
345+URI = get_uri()
346
347 FAKE_RECORD_TYPE = "http://example.org/test"
348
349
350=== modified file 'desktopcouch/start_local_couchdb.py'
351--- desktopcouch/start_local_couchdb.py 2009-08-21 22:45:24 +0000
352+++ desktopcouch/start_local_couchdb.py 2009-08-25 16:03:09 +0000
353@@ -34,14 +34,16 @@
354 """
355
356 from __future__ import with_statement
357-import os, subprocess, sys, glob
358+import os, subprocess, sys, glob, random, string
359 import desktopcouch
360 from desktopcouch import local_files
361 import xdg.BaseDirectory
362 import errno
363-import time
364+import time, gtk, gnomekeyring
365 from desktopcouch.records.server import CouchDatabase
366
367+ACCEPTABLE_USERNAME_PASSWORD_CHARS = string.lowercase + string.uppercase
368+
369 def dump_ini(data, filename):
370 """Dump INI data with sorted sections and keywords"""
371 fd = open(filename, 'w')
372@@ -56,13 +58,36 @@
373 fd.write("\n")
374 fd.close()
375
376-def create_ini_file(
377- ini_path=local_files.FILE_INI, db_dir=local_files.DIR_DB,
378- log_path=local_files.FILE_LOG, port="0"):
379+def create_ini_file(port="0"):
380 """Write CouchDB ini file if not already present"""
381- if os.path.exists(ini_path):
382- return
383- ini = {
384+ if os.path.exists(local_files.FILE_INI):
385+ # load the username and password from the keyring
386+ try:
387+ data = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,
388+ {'desktopcouch': 'basic'})
389+ except gnomekeyring.NoMatchError:
390+ data = None
391+ if data:
392+ username, password = data[0].secret.split(":")
393+ return username, password
394+ # otherwise fall through; for some reason the access details aren't
395+ # in the keyring, so re-create the ini file and do it all again
396+
397+ # randomly generate tokens and usernames
398+ def make_random_string(count):
399+ return ''.join([
400+ random.SystemRandom().choice(ACCEPTABLE_USERNAME_PASSWORD_CHARS)
401+ for x in range(count)])
402+
403+ admin_account_username = make_random_string(10)
404+ admin_account_basic_auth_password = make_random_string(10)
405+ consumer_key = make_random_string(10)
406+ consumer_secret = make_random_string(10)
407+ token = make_random_string(10)
408+ token_secret = make_random_string(10)
409+ db_dir = local_files.DIR_DB
410+
411+ local = {
412 'couchdb': {
413 'database_dir': db_dir,
414 'view_index_dir': db_dir,
415@@ -72,15 +97,50 @@
416 'port': port,
417 },
418 'log': {
419- 'file': log_path,
420+ 'file': local_files.FILE_LOG,
421 'level': 'info',
422 },
423+ ## 'admins': {
424+ ## admin_account_username: admin_account_basic_auth_password
425+ ## },
426+ 'oauth_consumer_secrets': {
427+ consumer_key: consumer_secret
428+ },
429+ 'oauth_token_secrets': {
430+ token: token_secret
431+ },
432+ 'oauth_token_users': {
433+ token: admin_account_username
434+ },
435+ ## 'couch_httpd_auth': {
436+ ## 'require_valid_user': 'true'
437+ ## },
438 }
439- dump_ini(ini, ini_path)
440-
441-def run_couchdb(exec_command=local_files.COUCH_EXEC_COMMAND):
442+
443+ dump_ini(local, local_files.FILE_INI)
444+ # save admin account details in keyring
445+ item_id = gnomekeyring.item_create_sync(
446+ None,
447+ gnomekeyring.ITEM_GENERIC_SECRET,
448+ 'Desktop Couch user authentication',
449+ {'desktopcouch': 'basic'},
450+ "%s:%s" % (
451+ admin_account_username, admin_account_basic_auth_password),
452+ True)
453+ # and oauth tokens
454+ item_id = gnomekeyring.item_create_sync(
455+ None,
456+ gnomekeyring.ITEM_GENERIC_SECRET,
457+ 'Desktop Couch user authentication',
458+ {'desktopcouch': 'oauth'},
459+ "%s:%s:%s:%s" % (
460+ consumer_key, consumer_secret, token, token_secret),
461+ True)
462+ return (admin_account_username, admin_account_basic_auth_password)
463+
464+def run_couchdb():
465 """Actually start the CouchDB process"""
466- local_exec = exec_command + ['-b']
467+ local_exec = local_files.COUCH_EXEC_COMMAND + ['-b']
468 try:
469 # subprocess is buggy. Chad patched, but that takes time to propagate.
470 proc = subprocess.Popen(local_exec)
471@@ -142,7 +202,7 @@
472 # than inefficiently just overwriting it regardless
473 db.add_view(view_name, mapjs, reducejs, dd_name)
474
475-def write_bookmark_file():
476+def write_bookmark_file(username, password):
477 """Write out an HTML document that the user can bookmark to find their DB"""
478 bookmark_file = os.path.join(local_files.DIR_DB, "couchdb.html")
479
480@@ -165,12 +225,13 @@
481 for retry in xrange(10000, 0, -1):
482 pid = desktopcouch.find_pid(start_if_not_running=False)
483 try:
484- port = desktopcouch.find_port(pid)
485- break
486- except RuntimeError, e:
487+ port = desktopcouch.find_port()
488+ if not port is None:
489+ break
490+ except IOError:
491 if retry == 1:
492 raise
493- time.sleep(0.01)
494+ time.sleep(0.1)
495 continue
496
497 if port is None:
498@@ -182,21 +243,24 @@
499 pass
500 else:
501 fp = open(bookmark_file, "w")
502- fp.write(html.replace("[[COUCHDB_PORT]]", port))
503+ out = html.replace("[[COUCHDB_PORT]]", port)
504+ out = out.replace("[[COUCHDB_USERNAME]]", username)
505+ out = out.replace("[[COUCHDB_PASSWORD]]", password)
506+ fp.write(out)
507 fp.close()
508 print "Browse your desktop CouchDB at file://%s" % \
509 os.path.realpath(bookmark_file)
510
511 def start_couchdb():
512 """Execute each step to start a desktop CouchDB"""
513- create_ini_file()
514+ username, password = create_ini_file()
515 run_couchdb()
516 # Note that we do not call update_design_documents here. This is because
517 # Couch won't actually have started yet, so when update_design_documents
518- # calls the Records API, that will call back into get_pid and we end up
519- # starting Couch again. Instead, get_pid calls update_design_documents
520+ # calls the Records API, that will call back into get_port and we end up
521+ # starting Couch again. Instead, get_port calls update_design_documents
522 # *after* Couch startup has occurred.
523- write_bookmark_file()
524+ write_bookmark_file(username, password)
525
526
527 if __name__ == "__main__":

Subscribers

People subscribed via source and target branches