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
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/local_files.py'
15--- desktopcouch/local_files.py 2009-08-05 11:18:46 +0000
16+++ desktopcouch/local_files.py 2009-08-25 12:58:26 +0000
17@@ -92,6 +92,42 @@
18
19 return chain
20
21+class NoOAuthTokenException(Exception):
22+ def __str__(self):
23+ return "OAuth details were not found in the ini file (%s)" % FILE_INI
24+
25+def get_oauth_tokens():
26+ """Return the OAuth tokens from the desktop Couch ini file.
27+ CouchDB OAuth is two-legged OAuth (not three-legged like most OAuth).
28+ We have one "consumer", defined by a consumer_key and a secret,
29+ and an "access token", defined by a token and a secret.
30+ The OAuth signature is created with reference to these and the requested
31+ URL.
32+ (More traditional 3-legged OAuth starts with a "request token" which is
33+ then used to procure an "access token". We do not require this.)
34+ """
35+ import ConfigParser
36+ c = ConfigParser.ConfigParser()
37+ # monkeypatch ConfigParser to stop it lower-casing option names
38+ c.optionxform = lambda s: s
39+ c.read(FILE_INI)
40+ try:
41+ oauth_token_secrets = c.items("oauth_token_secretsx")
42+ oauth_consumer_secrets = c.items("oauth_consumer_secrets")
43+ except ConfigParser.NoSectionError:
44+ raise NoOAuthTokenException
45+ if not oauth_token_secrets or not oauth_consumer_secrets:
46+ raise NoOAuthTokenException
47+ try:
48+ out = {
49+ "token": oauth_token_secrets[0][0],
50+ "token_secret": oauth_token_secrets[0][1],
51+ "consumer_key": oauth_consumer_secrets[0][0],
52+ "consumer_secret": oauth_consumer_secrets[0][1]
53+ }
54+ except IndexError:
55+ raise NoOAuthTokenException
56+ return out
57
58 # You will need to add -b or -k on the end of this
59 COUCH_EXEC_COMMAND = [COUCH_EXE, couch_chain_flag(), FILE_INI, '-p', FILE_PID,
60
61=== modified file 'desktopcouch/start_local_couchdb.py'
62--- desktopcouch/start_local_couchdb.py 2009-08-19 16:00:01 +0000
63+++ desktopcouch/start_local_couchdb.py 2009-08-20 12:55:54 +0000
64@@ -32,14 +32,16 @@
65 """
66
67 from __future__ import with_statement
68-import os, subprocess, sys, glob
69+import os, subprocess, sys, glob, random, string
70 import desktopcouch
71 from desktopcouch import local_files
72 import xdg.BaseDirectory
73 import errno
74-import time
75+import time, gtk, gnomekeyring
76 from desktopcouch.records.server import CouchDatabase
77
78+ACCEPTABLE_USERNAME_PASSWORD_CHARS = string.lowercase + string.uppercase
79+
80 def dump_ini(data, filename):
81 """Dump INI data with sorted sections and keywords"""
82 fd = open(filename, 'w')
83@@ -56,18 +58,31 @@
84
85 def create_ini_file():
86 """Write CouchDB ini file if not already present"""
87- # FIXME add update trigger folder
88- #update_trigger_dir = [
89- # 'lib', 'canonical', 'ubuntuone', 'cloud_server', 'update_triggers']
90- #
91- #timestamp_trigger = os.path.join(
92- # *update_trigger_dir + ['timestamp_trigger.py'])
93- #update_trigger = os.path.join(
94- # *update_trigger_dir + ['update_trigger.py'])
95-
96 if os.path.exists(local_files.FILE_INI):
97- return
98-
99+ # load the username and password from the keyring
100+ try:
101+ data = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,
102+ {'desktopcouch': 'basic'})
103+ except gnomekeyring.NoMatchError:
104+ data = None
105+ if data:
106+ username, password = data[0].secret.split(":")
107+ return username, password
108+ # otherwise fall through; for some reason the access details aren't
109+ # in the keyring, so re-create the ini file and do it all again
110+
111+ # randomly generate tokens and usernames
112+ def make_random_string(count):
113+ return ''.join([random.choice(ACCEPTABLE_USERNAME_PASSWORD_CHARS)
114+ for x in range(count)])
115+
116+ ADMIN_ACCOUNT_USERNAME = make_random_string(10)
117+ ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD = make_random_string(10)
118+ CONSUMER_KEY = make_random_string(10)
119+ CONSUMER_SECRET = make_random_string(10)
120+ TOKEN = make_random_string(10)
121+ TOKEN_SECRET = make_random_string(10)
122+
123 local = {
124 'couchdb': {
125 'database_dir': local_files.DIR_DB,
126@@ -81,9 +96,43 @@
127 'file': local_files.FILE_LOG,
128 'level': 'info',
129 },
130+ 'admins': {
131+ ADMIN_ACCOUNT_USERNAME: ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD
132+ },
133+ 'oauth_consumer_secrets': {
134+ CONSUMER_KEY: CONSUMER_SECRET
135+ },
136+ 'oauth_token_secrets': {
137+ TOKEN: TOKEN_SECRET
138+ },
139+ 'oauth_token_users': {
140+ TOKEN: ADMIN_ACCOUNT_USERNAME
141+ },
142+ 'couch_httpd_auth': {
143+ 'require_valid_user': 'true'
144+ }
145 }
146
147 dump_ini(local, local_files.FILE_INI)
148+ # save admin account details in keyring
149+ item_id = gnomekeyring.item_create_sync(
150+ None,
151+ gnomekeyring.ITEM_GENERIC_SECRET,
152+ 'Desktop Couch user authentication',
153+ {'desktopcouch': 'basic'},
154+ "%s:%s" % (ADMIN_ACCOUNT_USERNAME, ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD),
155+ True)
156+ # and oauth tokens
157+ item_id = gnomekeyring.item_create_sync(
158+ None,
159+ gnomekeyring.ITEM_GENERIC_SECRET,
160+ 'Desktop Couch user authentication',
161+ {'desktopcouch': 'oauth'},
162+ "%s:%s:%s:%s" % (CONSUMER_KEY, CONSUMER_SECRET, TOKEN, TOKEN_SECRET),
163+ True)
164+
165+
166+ return (ADMIN_ACCOUNT_USERNAME, ADMIN_ACCOUNT_BASIC_AUTH_PASSWORD)
167
168 def run_couchdb():
169 """Actually start the CouchDB process"""
170@@ -146,7 +195,7 @@
171 # than inefficiently just overwriting it regardless
172 db.add_view(view_name, mapjs, reducejs, dd_name)
173
174-def write_bookmark_file():
175+def write_bookmark_file(username, password):
176 """Write out an HTML document that the user can bookmark to find their DB"""
177 bookmark_file = os.path.join(local_files.DIR_DB, "couchdb.html")
178
179@@ -182,21 +231,24 @@
180 pass
181 else:
182 fp = open(bookmark_file, "w")
183- fp.write(html.replace("[[COUCHDB_PORT]]", port))
184+ out = html.replace("[[COUCHDB_PORT]]", port)
185+ out = out.replace("[[COUCHDB_USERNAME]]", username)
186+ out = out.replace("[[COUCHDB_PASSWORD]]", password)
187+ fp.write(out)
188 fp.close()
189 print "Browse your desktop CouchDB at file://%s" % \
190 os.path.realpath(bookmark_file)
191
192 def start_couchdb():
193 """Execute each step to start a desktop CouchDB"""
194- create_ini_file()
195+ username, password = create_ini_file()
196 run_couchdb()
197 # Note that we do not call update_design_documents here. This is because
198 # Couch won't actually have started yet, so when update_design_documents
199 # calls the Records API, that will call back into get_pid and we end up
200 # starting Couch again. Instead, get_pid calls update_design_documents
201 # *after* Couch startup has occurred.
202- write_bookmark_file()
203+ write_bookmark_file(username, password)
204
205
206 if __name__ == "__main__":

Subscribers

People subscribed via source and target branches