Merge lp://qastaging/~sil/desktopcouch/get-oauth-tokens-function into lp://qastaging/desktopcouch

Proposed by Stuart Langridge
Status: Rejected
Rejected by: Elliot Murphy
Proposed branch: lp://qastaging/~sil/desktopcouch/get-oauth-tokens-function
Merge into: lp://qastaging/desktopcouch
Diff against target: None lines
To merge this branch: bzr merge lp://qastaging/~sil/desktopcouch/get-oauth-tokens-function
Reviewer Review Type Date Requested Status
Tim Cole (community) Needs Fixing
Review via email: mp+10653@code.qastaging.launchpad.net
To post a comment you must log in.
Revision history for this message
Stuart Langridge (sil) wrote :

Add a function to retrieve the OAuth token details for this desktop couch server from the ini file, which will be required by the pairing process (so they can be handed to a paired server over the network).

Revision history for this message
Tim Cole (tcole) wrote :

***PLEASE*** use a random.SystemRandom instance and call .choice() on that rather than using random/random.Random to generate anything related to credentials.

review: Needs Fixing
Revision history for this message
Stuart Langridge (sil) wrote :

> ***PLEASE*** use a random.SystemRandom instance and call .choice() on that
> rather than using random/random.Random to generate anything related to
> credentials.

Fixed in a new branch, lp:~sil/desktopcouch/not-so-random (because this branch didn't actually add that code, this is a branch of another unmerged branch that added it).

39. By Stuart Langridge

typo

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'data/couchdb.tmpl'
--- data/couchdb.tmpl 2009-07-14 13:53:21 +0000
+++ data/couchdb.tmpl 2009-08-20 12:55:54 +0000
@@ -31,7 +31,7 @@
31come back to browse your CouchDB again.</p>31come back to browse your CouchDB again.</p>
32<p>Don't bookmark the CouchDB page itself, because its location may change!</p>32<p>Don't bookmark the CouchDB page itself, because its location may change!</p>
33<p>Taking you to your Desktop CouchDB in <span>30</span> seconds...33<p>Taking you to your Desktop CouchDB in <span>30</span> seconds...
34<a id="there" href="http://localhost:[[COUCHDB_PORT]]/_utils">take me34<a id="there" href="http://[[COUCHDB_USERNAME]]:[[COUCHDB_PASSWORD]]@localhost:[[COUCHDB_PORT]]/_utils">take me
35there straight away from now on</a> (remember to bookmark this page first!)</p>35there straight away from now on</a> (remember to bookmark this page first!)</p>
36</body>36</body>
37</html>37</html>
3838
=== modified file 'desktopcouch/local_files.py'
--- desktopcouch/local_files.py 2009-08-05 11:18:46 +0000
+++ desktopcouch/local_files.py 2009-08-25 12:58:26 +0000
@@ -92,6 +92,42 @@
9292
93 return chain93 return chain
9494
95class NoOAuthTokenException(Exception):
96 def __str__(self):
97 return "OAuth details were not found in the ini file (%s)" % FILE_INI
98
99def get_oauth_tokens():
100 """Return the OAuth tokens from the desktop Couch ini file.
101 CouchDB OAuth is two-legged OAuth (not three-legged like most OAuth).
102 We have one "consumer", defined by a consumer_key and a secret,
103 and an "access token", defined by a token and a secret.
104 The OAuth signature is created with reference to these and the requested
105 URL.
106 (More traditional 3-legged OAuth starts with a "request token" which is
107 then used to procure an "access token". We do not require this.)
108 """
109 import ConfigParser
110 c = ConfigParser.ConfigParser()
111 # monkeypatch ConfigParser to stop it lower-casing option names
112 c.optionxform = lambda s: s
113 c.read(FILE_INI)
114 try:
115 oauth_token_secrets = c.items("oauth_token_secretsx")
116 oauth_consumer_secrets = c.items("oauth_consumer_secrets")
117 except ConfigParser.NoSectionError:
118 raise NoOAuthTokenException
119 if not oauth_token_secrets or not oauth_consumer_secrets:
120 raise NoOAuthTokenException
121 try:
122 out = {
123 "token": oauth_token_secrets[0][0],
124 "token_secret": oauth_token_secrets[0][1],
125 "consumer_key": oauth_consumer_secrets[0][0],
126 "consumer_secret": oauth_consumer_secrets[0][1]
127 }
128 except IndexError:
129 raise NoOAuthTokenException
130 return out
95131
96# You will need to add -b or -k on the end of this132# You will need to add -b or -k on the end of this
97COUCH_EXEC_COMMAND = [COUCH_EXE, couch_chain_flag(), FILE_INI, '-p', FILE_PID,133COUCH_EXEC_COMMAND = [COUCH_EXE, couch_chain_flag(), FILE_INI, '-p', FILE_PID,
98134
=== modified file 'desktopcouch/start_local_couchdb.py'
--- desktopcouch/start_local_couchdb.py 2009-08-19 16:00:01 +0000
+++ desktopcouch/start_local_couchdb.py 2009-08-20 12:55:54 +0000
@@ -32,14 +32,16 @@
32"""32"""
3333
34from __future__ import with_statement34from __future__ import with_statement
35import os, subprocess, sys, glob35import os, subprocess, sys, glob, random, string
36import desktopcouch36import desktopcouch
37from desktopcouch import local_files37from desktopcouch import local_files
38import xdg.BaseDirectory38import xdg.BaseDirectory
39import errno39import errno
40import time40import time, gtk, gnomekeyring
41from desktopcouch.records.server import CouchDatabase41from desktopcouch.records.server import CouchDatabase
4242
43ACCEPTABLE_USERNAME_PASSWORD_CHARS = string.lowercase + string.uppercase
44
43def dump_ini(data, filename):45def dump_ini(data, filename):
44 """Dump INI data with sorted sections and keywords"""46 """Dump INI data with sorted sections and keywords"""
45 fd = open(filename, 'w')47 fd = open(filename, 'w')
@@ -56,18 +58,31 @@
5658
57def create_ini_file():59def create_ini_file():
58 """Write CouchDB ini file if not already present"""60 """Write CouchDB ini file if not already present"""
59 # FIXME add update trigger folder
60 #update_trigger_dir = [
61 # 'lib', 'canonical', 'ubuntuone', 'cloud_server', 'update_triggers']
62 #
63 #timestamp_trigger = os.path.join(
64 # *update_trigger_dir + ['timestamp_trigger.py'])
65 #update_trigger = os.path.join(
66 # *update_trigger_dir + ['update_trigger.py'])
67
68 if os.path.exists(local_files.FILE_INI):61 if os.path.exists(local_files.FILE_INI):
69 return62 # load the username and password from the keyring
7063 try:
64 data = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,
65 {'desktopcouch': 'basic'})
66 except gnomekeyring.NoMatchError:
67 data = None
68 if data:
69 username, password = data[0].secret.split(":")
70 return username, password
71 # otherwise fall through; for some reason the access details aren't
72 # in the keyring, so re-create the ini file and do it all again
73
74 # randomly generate tokens and usernames
75 def make_random_string(count):
76 return ''.join([random.choice(ACCEPTABLE_USERNAME_PASSWORD_CHARS)
77 for x in range(count)])
78
79 ADMIN_ACCOUNT_USERNAME = make_random_string(10)
80 ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD = make_random_string(10)
81 CONSUMER_KEY = make_random_string(10)
82 CONSUMER_SECRET = make_random_string(10)
83 TOKEN = make_random_string(10)
84 TOKEN_SECRET = make_random_string(10)
85
71 local = {86 local = {
72 'couchdb': {87 'couchdb': {
73 'database_dir': local_files.DIR_DB,88 'database_dir': local_files.DIR_DB,
@@ -81,9 +96,43 @@
81 'file': local_files.FILE_LOG,96 'file': local_files.FILE_LOG,
82 'level': 'info',97 'level': 'info',
83 },98 },
99 'admins': {
100 ADMIN_ACCOUNT_USERNAME: ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD
101 },
102 'oauth_consumer_secrets': {
103 CONSUMER_KEY: CONSUMER_SECRET
104 },
105 'oauth_token_secrets': {
106 TOKEN: TOKEN_SECRET
107 },
108 'oauth_token_users': {
109 TOKEN: ADMIN_ACCOUNT_USERNAME
110 },
111 'couch_httpd_auth': {
112 'require_valid_user': 'true'
113 }
84 }114 }
85115
86 dump_ini(local, local_files.FILE_INI)116 dump_ini(local, local_files.FILE_INI)
117 # save admin account details in keyring
118 item_id = gnomekeyring.item_create_sync(
119 None,
120 gnomekeyring.ITEM_GENERIC_SECRET,
121 'Desktop Couch user authentication',
122 {'desktopcouch': 'basic'},
123 "%s:%s" % (ADMIN_ACCOUNT_USERNAME, ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD),
124 True)
125 # and oauth tokens
126 item_id = gnomekeyring.item_create_sync(
127 None,
128 gnomekeyring.ITEM_GENERIC_SECRET,
129 'Desktop Couch user authentication',
130 {'desktopcouch': 'oauth'},
131 "%s:%s:%s:%s" % (CONSUMER_KEY, CONSUMER_SECRET, TOKEN, TOKEN_SECRET),
132 True)
133
134
135 return (ADMIN_ACCOUNT_USERNAME, ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD)
87136
88def run_couchdb():137def run_couchdb():
89 """Actually start the CouchDB process"""138 """Actually start the CouchDB process"""
@@ -146,7 +195,7 @@
146 # than inefficiently just overwriting it regardless195 # than inefficiently just overwriting it regardless
147 db.add_view(view_name, mapjs, reducejs, dd_name)196 db.add_view(view_name, mapjs, reducejs, dd_name)
148197
149def write_bookmark_file():198def write_bookmark_file(username, password):
150 """Write out an HTML document that the user can bookmark to find their DB"""199 """Write out an HTML document that the user can bookmark to find their DB"""
151 bookmark_file = os.path.join(local_files.DIR_DB, "couchdb.html")200 bookmark_file = os.path.join(local_files.DIR_DB, "couchdb.html")
152201
@@ -182,21 +231,24 @@
182 pass231 pass
183 else:232 else:
184 fp = open(bookmark_file, "w")233 fp = open(bookmark_file, "w")
185 fp.write(html.replace("[[COUCHDB_PORT]]", port))234 out = html.replace("[[COUCHDB_PORT]]", port)
235 out = out.replace("[[COUCHDB_USERNAME]]", username)
236 out = out.replace("[[COUCHDB_PASSWORD]]", password)
237 fp.write(out)
186 fp.close()238 fp.close()
187 print "Browse your desktop CouchDB at file://%s" % \239 print "Browse your desktop CouchDB at file://%s" % \
188 os.path.realpath(bookmark_file)240 os.path.realpath(bookmark_file)
189241
190def start_couchdb():242def start_couchdb():
191 """Execute each step to start a desktop CouchDB"""243 """Execute each step to start a desktop CouchDB"""
192 create_ini_file()244 username, password = create_ini_file()
193 run_couchdb()245 run_couchdb()
194 # Note that we do not call update_design_documents here. This is because246 # Note that we do not call update_design_documents here. This is because
195 # Couch won't actually have started yet, so when update_design_documents247 # Couch won't actually have started yet, so when update_design_documents
196 # calls the Records API, that will call back into get_pid and we end up248 # calls the Records API, that will call back into get_pid and we end up
197 # starting Couch again. Instead, get_pid calls update_design_documents249 # starting Couch again. Instead, get_pid calls update_design_documents
198 # *after* Couch startup has occurred.250 # *after* Couch startup has occurred.
199 write_bookmark_file()251 write_bookmark_file(username, password)
200252
201253
202if __name__ == "__main__":254if __name__ == "__main__":

Subscribers

People subscribed via source and target branches