Merge lp:~mvo/software-center/merge-prev-purchases-lp969273 into lp:software-center
- merge-prev-purchases-lp969273
- Merge into trunk
Proposed by
Michael Vogt
Status: | Merged |
---|---|
Merged at revision: | 3180 |
Proposed branch: | lp:~mvo/software-center/merge-prev-purchases-lp969273 |
Merge into: | lp:software-center |
Diff against target: |
1157 lines (+408/-286) 15 files modified
softwarecenter/backend/login.py (+6/-0) softwarecenter/backend/login_sso.py (+24/-2) softwarecenter/backend/scagent.py (+2/-1) softwarecenter/backend/spawn_helper.py (+3/-0) softwarecenter/backend/ubuntusso.py (+106/-9) softwarecenter/db/database.py (+12/-0) softwarecenter/db/update.py (+41/-50) softwarecenter/enums.py (+0/-1) softwarecenter/ui/gtk3/app.py (+54/-57) tests/gtk3/test_purchase.py (+19/-16) tests/test_database.py (+10/-2) tests/test_reinstall_purchased.py (+112/-63) tests/test_ubuntu_sso_api.py (+3/-3) utils/piston-helpers/piston_generic_helper.py (+12/-82) utils/update-software-center-agent (+4/-0) |
To merge this branch: | bzr merge lp:~mvo/software-center/merge-prev-purchases-lp969273 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Lasker (community) | Approve | ||
Review via email: mp+124417@code.launchpad.net |
Commit message
Description of the change
This branch changes the way the reinstall previous purchases is done. It merges them on update of the
update-
in-memory database. This fixes the double entries in the DB and has the nice side-effect that items already
purchased will be displayed as already purchased instead of showing up with a price.
To post a comment you must log in.
- 3187. By Michael Vogt
-
tests/gtk3/
test_purchase. py: update test to latest code - 3188. By Michael Vogt
-
trivial pep8 fixes
Revision history for this message
Michael Vogt (mvo) wrote : | # |
Revision history for this message
Gary Lasker (gary-lasker) wrote : | # |
I looked this over carefully and tested reinstall previous purchases under various sequences of steps. I like the new query for a purchased item, it's simpler and it's very fast. All unit test changes look good and all changed tests pass for me.
Thanks, Michael!
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'softwarecenter/backend/login.py' |
2 | --- softwarecenter/backend/login.py 2012-03-19 14:20:55 +0000 |
3 | +++ softwarecenter/backend/login.py 2012-09-14 13:44:26 +0000 |
4 | @@ -49,5 +49,11 @@ |
5 | def login(self, username=None, password=None): |
6 | raise NotImplemented |
7 | |
8 | + def login_or_register(self): |
9 | + raise NotImplemented |
10 | + |
11 | + def find_credentials(self): |
12 | + raise NotImplemented |
13 | + |
14 | def cancel_login(self): |
15 | self.emit("login-canceled") |
16 | |
17 | === modified file 'softwarecenter/backend/login_sso.py' |
18 | --- softwarecenter/backend/login_sso.py 2012-07-09 13:33:51 +0000 |
19 | +++ softwarecenter/backend/login_sso.py 2012-09-14 13:44:26 +0000 |
20 | @@ -31,6 +31,12 @@ |
21 | from softwarecenter.utils import utf8 |
22 | from login import LoginBackend |
23 | |
24 | +from ubuntu_sso import ( |
25 | + DBUS_BUS_NAME, |
26 | + DBUS_CREDENTIALS_IFACE, |
27 | + DBUS_CREDENTIALS_PATH, |
28 | + ) |
29 | + |
30 | # mostly for testing |
31 | from fake_review_settings import FakeReviewSettings, network_delay |
32 | |
33 | @@ -44,10 +50,15 @@ |
34 | self.appname = appname |
35 | self.help_text = help_text |
36 | self.bus = dbus.SessionBus() |
37 | - self.proxy = self.bus.get_object( |
38 | - 'com.ubuntu.sso', '/com/ubuntu/sso/credentials') |
39 | + obj = self.bus.get_object(bus_name=DBUS_BUS_NAME, |
40 | + object_path=DBUS_CREDENTIALS_PATH, |
41 | + follow_name_owner_changes=True) |
42 | + self.proxy = dbus.Interface(object=obj, |
43 | + dbus_interface=DBUS_CREDENTIALS_IFACE) |
44 | self.proxy.connect_to_signal("CredentialsFound", |
45 | self._on_credentials_found) |
46 | + self.proxy.connect_to_signal("CredentialsNotFound", |
47 | + self._on_credentials_not_found) |
48 | self.proxy.connect_to_signal("CredentialsError", |
49 | self._on_credentials_error) |
50 | self.proxy.connect_to_signal("AuthorizationDenied", |
51 | @@ -64,6 +75,11 @@ |
52 | p['window_id'] = self._window_id |
53 | return p |
54 | |
55 | + def find_credentials(self): |
56 | + LOG.debug("find_crendentials()") |
57 | + self._credentials = None |
58 | + self.proxy.find_credentials(self.appname, self._get_params()) |
59 | + |
60 | def login(self, username=None, password=None): |
61 | LOG.debug("login()") |
62 | self._credentials = None |
63 | @@ -74,6 +90,12 @@ |
64 | self._credentials = None |
65 | self.proxy.register(self.appname, self._get_params()) |
66 | |
67 | + def _on_credentials_not_found(self, app_name): |
68 | + LOG.debug("_on_credentials_found for '%s'" % app_name) |
69 | + if app_name != self.appname: |
70 | + return |
71 | + self.emit("login-failed") |
72 | + |
73 | def _on_credentials_found(self, app_name, credentials): |
74 | LOG.debug("_on_credentials_found for '%s'" % app_name) |
75 | if app_name != self.appname: |
76 | |
77 | === modified file 'softwarecenter/backend/scagent.py' |
78 | --- softwarecenter/backend/scagent.py 2012-08-29 12:07:14 +0000 |
79 | +++ softwarecenter/backend/scagent.py 2012-09-14 13:44:26 +0000 |
80 | @@ -93,13 +93,14 @@ |
81 | def _on_query_available_data(self, spawner, piston_available): |
82 | self.emit("available", piston_available) |
83 | |
84 | - def query_available_for_me(self): |
85 | + def query_available_for_me(self, no_relogin=False): |
86 | spawner = SpawnHelper() |
87 | spawner.parent_xid = self.xid |
88 | spawner.ignore_cache = self.ignore_cache |
89 | spawner.connect("data-available", self._on_query_available_for_me_data) |
90 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) |
91 | spawner.needs_auth = True |
92 | + spawner.no_relogin = no_relogin |
93 | spawner.run_generic_piston_helper( |
94 | "SoftwareCenterAgentAPI", "subscriptions_for_me", |
95 | complete_only=True) |
96 | |
97 | === modified file 'softwarecenter/backend/spawn_helper.py' |
98 | --- softwarecenter/backend/spawn_helper.py 2012-05-16 15:52:07 +0000 |
99 | +++ softwarecenter/backend/spawn_helper.py 2012-09-14 13:44:26 +0000 |
100 | @@ -64,6 +64,7 @@ |
101 | self._child_watch = None |
102 | self._cmd = None |
103 | self.needs_auth = False |
104 | + self.no_relogin = False |
105 | self.ignore_cache = False |
106 | self.parent_xid = None |
107 | |
108 | @@ -76,6 +77,8 @@ |
109 | cmd.append("--needs-auth") |
110 | if self.ignore_cache: |
111 | cmd.append("--ignore-cache") |
112 | + if self.no_relogin: |
113 | + cmd.append("--no-relogin") |
114 | if self.parent_xid: |
115 | cmd.append("--parent-xid") |
116 | cmd.append(str(self.parent_xid)) |
117 | |
118 | === modified file 'softwarecenter/backend/ubuntusso.py' |
119 | --- softwarecenter/backend/ubuntusso.py 2012-08-30 08:57:29 +0000 |
120 | +++ softwarecenter/backend/ubuntusso.py 2012-09-14 13:44:26 +0000 |
121 | @@ -21,22 +21,44 @@ |
122 | |
123 | |
124 | from gi.repository import GObject |
125 | +from gettext import gettext as _ |
126 | |
127 | import logging |
128 | import os |
129 | |
130 | +import piston_mini_client.auth |
131 | +import piston_mini_client.failhandlers |
132 | + |
133 | + |
134 | import softwarecenter.paths |
135 | |
136 | # mostly for testing |
137 | from fake_review_settings import FakeReviewSettings, network_delay |
138 | from spawn_helper import SpawnHelper |
139 | from softwarecenter.config import get_config |
140 | +from softwarecenter.backend.login_sso import get_sso_backend |
141 | + |
142 | +from softwarecenter.backend.piston.ubuntusso_pristine import ( |
143 | + UbuntuSsoAPI as PristineUbuntuSsoAPI, |
144 | + ) |
145 | +# patch default_service_root to the one we use |
146 | +from softwarecenter.enums import UBUNTU_SSO_SERVICE |
147 | +# *Don't* append /api/1.0, as it's already included in UBUNTU_SSO_SERVICE |
148 | +PristineUbuntuSsoAPI.default_service_root = UBUNTU_SSO_SERVICE |
149 | + |
150 | +from softwarecenter.enums import (SOFTWARE_CENTER_NAME_KEYRING, |
151 | + SOFTWARE_CENTER_SSO_DESCRIPTION, |
152 | + ) |
153 | +from softwarecenter.utils import clear_token_from_ubuntu_sso_sync |
154 | |
155 | LOG = logging.getLogger(__name__) |
156 | |
157 | |
158 | -class UbuntuSSOAPI(GObject.GObject): |
159 | - """ Ubuntu SSO interface using the oauth token from the keyring """ |
160 | +class UbuntuSSO(GObject.GObject): |
161 | + """ Ubuntu SSO interface using the oauth token from the keyring |
162 | + |
163 | + The methods that work syncronous are suffixed with _sync() |
164 | + """ |
165 | |
166 | __gsignals__ = { |
167 | "whoami": (GObject.SIGNAL_RUN_LAST, |
168 | @@ -49,8 +71,11 @@ |
169 | ), |
170 | } |
171 | |
172 | - def __init__(self): |
173 | + def __init__(self, xid=0): |
174 | GObject.GObject.__init__(self) |
175 | + self.oauth = None |
176 | + self.xid = xid |
177 | + self.loop = GObject.MainLoop(GObject.main_context_default()) |
178 | |
179 | def _on_whoami_data(self, spawner, piston_whoami): |
180 | # once we have data, make sure to save it |
181 | @@ -71,11 +96,83 @@ |
182 | spawner.needs_auth = True |
183 | spawner.run_generic_piston_helper("UbuntuSsoAPI", "whoami") |
184 | |
185 | - |
186 | -class UbuntuSSOAPIFake(UbuntuSSOAPI): |
187 | + def _login_successful(self, sso_backend, oauth_result): |
188 | + LOG.debug("_login_successful") |
189 | + self.oauth = oauth_result |
190 | + self.loop.quit() |
191 | + |
192 | + # sync calls |
193 | + def verify_token_sync(self, token): |
194 | + """ Verify that the token is valid |
195 | + |
196 | + Note that this may raise httplib2 exceptions if the server |
197 | + is not reachable |
198 | + """ |
199 | + LOG.debug("verify_token") |
200 | + auth = piston_mini_client.auth.OAuthAuthorizer( |
201 | + token["token"], token["token_secret"], |
202 | + token["consumer_key"], token["consumer_secret"]) |
203 | + api = PristineUbuntuSsoAPI(auth=auth) |
204 | + try: |
205 | + res = api.whoami() |
206 | + except piston_mini_client.failhandlers.APIError as e: |
207 | + LOG.exception("api.whoami failed with APIError: '%s'" % e) |
208 | + return False |
209 | + return len(res) > 0 |
210 | + |
211 | + def clear_token(self): |
212 | + clear_token_from_ubuntu_sso_sync(SOFTWARE_CENTER_NAME_KEYRING) |
213 | + |
214 | + def _get_sso_backend_and_connect(self): |
215 | + sso = get_sso_backend( |
216 | + self.xid, |
217 | + SOFTWARE_CENTER_NAME_KEYRING, |
218 | + _(SOFTWARE_CENTER_SSO_DESCRIPTION)) |
219 | + sso.connect("login-successful", self._login_successful) |
220 | + sso.connect("login-failed", lambda s: self.loop.quit()) |
221 | + sso.connect("login-canceled", lambda s: self.loop.quit()) |
222 | + return sso |
223 | + |
224 | + def find_oauth_token_sync(self): |
225 | + self.oauth = None |
226 | + sso = self. _get_sso_backend_and_connect() |
227 | + sso.find_credentials() |
228 | + self.loop.run() |
229 | + return self.oauth |
230 | + |
231 | + def get_oauth_token_sync(self): |
232 | + self.oauth = None |
233 | + sso = self. _get_sso_backend_and_connect() |
234 | + sso.login_or_register() |
235 | + self.loop.run() |
236 | + return self.oauth |
237 | + |
238 | + def get_oauth_token_and_verify_sync(self, no_relogin=False): |
239 | + token = self.get_oauth_token_sync() |
240 | + # check if the token is valid and reset it if it is not |
241 | + if token: |
242 | + # verify token will return false if there is a API error, |
243 | + # but there maybe httplib2 errors if there is no network, |
244 | + # so ignore them |
245 | + try: |
246 | + if not self.verify_token_sync(token): |
247 | + attempt_relogin = not no_relogin |
248 | + if attempt_relogin: |
249 | + self.clear_token() |
250 | + # re-trigger login once |
251 | + token = self.get_oauth_token_sync() |
252 | + else: |
253 | + return None |
254 | + except Exception as e: |
255 | + LOG.warn( |
256 | + "token could not be verified (network problem?): %s" % e) |
257 | + return token |
258 | + |
259 | + |
260 | +class UbuntuSSOAPIFake(UbuntuSSO): |
261 | |
262 | def __init__(self): |
263 | - UbuntuSSOAPI.__init__(self) |
264 | + UbuntuSSO.__init__(self) |
265 | self._fake_settings = FakeReviewSettings() |
266 | |
267 | @network_delay |
268 | @@ -110,7 +207,7 @@ |
269 | ubuntu_sso_class = UbuntuSSOAPIFake() |
270 | LOG.warn('Using fake Ubuntu SSO API. Only meant for testing purposes') |
271 | else: |
272 | - ubuntu_sso_class = UbuntuSSOAPI() |
273 | + ubuntu_sso_class = UbuntuSSO() |
274 | return ubuntu_sso_class |
275 | |
276 | |
277 | @@ -133,6 +230,7 @@ |
278 | password = sys.stdin.readline().strip() |
279 | sso.login(user, password) |
280 | |
281 | + |
282 | # interactive test code |
283 | if __name__ == "__main__": |
284 | def _whoami(sso, result): |
285 | @@ -145,7 +243,7 @@ |
286 | |
287 | def _dbus_maybe_login_successful(ssologin, oauth_result): |
288 | print "got token, verify it now" |
289 | - sso = UbuntuSSOAPI() |
290 | + sso = UbuntuSSO() |
291 | sso.connect("whoami", _whoami) |
292 | sso.connect("error", _error) |
293 | sso.whoami() |
294 | @@ -154,7 +252,6 @@ |
295 | logging.basicConfig(level=logging.DEBUG) |
296 | softwarecenter.paths.datadir = "./data" |
297 | |
298 | - from login_sso import get_sso_backend |
299 | backend = get_sso_backend("", "appname", "help_text") |
300 | backend.connect("login-successful", _dbus_maybe_login_successful) |
301 | backend.login_or_register() |
302 | |
303 | === modified file 'softwarecenter/db/database.py' |
304 | --- softwarecenter/db/database.py 2012-09-12 12:41:05 +0000 |
305 | +++ softwarecenter/db/database.py 2012-09-14 13:44:26 +0000 |
306 | @@ -43,6 +43,18 @@ |
307 | LOG = logging.getLogger(__name__) |
308 | |
309 | |
310 | +def get_reinstall_previous_purchases_query(): |
311 | + """Return a query to get applications purchased |
312 | + |
313 | + :return: a xapian query to get all the apps that are purchaed |
314 | + """ |
315 | + # this query will give us all documents that have a purchase date != "" |
316 | + query = xapian.Query(xapian.Query.OP_VALUE_GE, |
317 | + XapianValues.PURCHASED_DATE, |
318 | + "1") |
319 | + return query |
320 | + |
321 | + |
322 | def parse_axi_values_file(filename="/var/lib/apt-xapian-index/values"): |
323 | """ parse the apt-xapian-index "values" file and provide the |
324 | information in the self._axi_values dict |
325 | |
326 | === modified file 'softwarecenter/db/update.py' |
327 | --- softwarecenter/db/update.py 2012-09-10 09:40:48 +0000 |
328 | +++ softwarecenter/db/update.py 2012-09-14 13:44:26 +0000 |
329 | @@ -30,9 +30,13 @@ |
330 | from gi.repository import GObject |
331 | from piston_mini_client import PistonResponseObject |
332 | |
333 | +from softwarecenter.backend.scagent import SoftwareCenterAgent |
334 | +from softwarecenter.backend.ubuntusso import UbuntuSSO |
335 | from softwarecenter.distro import get_distro |
336 | from softwarecenter.utils import utf8 |
337 | |
338 | +from gettext import gettext as _ |
339 | + |
340 | # py3 compat |
341 | try: |
342 | from configparser import RawConfigParser, NoOptionError |
343 | @@ -49,7 +53,6 @@ |
344 | import pickle |
345 | |
346 | |
347 | -from gettext import gettext as _ |
348 | from glob import glob |
349 | from urlparse import urlparse |
350 | |
351 | @@ -59,7 +62,6 @@ |
352 | AppInfoFields, |
353 | AVAILABLE_FOR_PURCHASE_MAGIC_CHANNEL_NAME, |
354 | DB_SCHEMA_VERSION, |
355 | - PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME, |
356 | XapianValues, |
357 | ) |
358 | from softwarecenter.db.database import parse_axi_values_file |
359 | @@ -672,8 +674,6 @@ |
360 | # gets confused about (appname, pkgname) duplication |
361 | self.sca_application.name = utf8(_("%s (already purchased)")) % utf8( |
362 | self.sca_application.name) |
363 | - self.sca_application.channel = ( |
364 | - PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME) |
365 | for attr_name in ('license_key', 'license_key_path'): |
366 | attr = getattr(self.sca_subscription, attr_name, self.NOT_DEFINED) |
367 | if attr is self.NOT_DEFINED: |
368 | @@ -1007,44 +1007,6 @@ |
369 | return True |
370 | |
371 | |
372 | -def add_from_purchased_but_needs_reinstall_data( |
373 | - purchased_but_may_need_reinstall_list, db, cache): |
374 | - """Add application that have been purchased but may require a reinstall |
375 | - |
376 | - This adds a inmemory database to the main db with the special |
377 | - PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME channel prefix |
378 | - |
379 | - :return: a xapian query to get all the apps that need reinstall |
380 | - """ |
381 | - # magic |
382 | - db_purchased = xapian.inmemory_open() |
383 | - # go over the items we have |
384 | - for item in purchased_but_may_need_reinstall_list: |
385 | - # FIXME: what to do with duplicated entries? we will end |
386 | - # up with two xapian.Document, one for the for-pay |
387 | - # and one for the availalbe one from s-c-agent |
388 | - #try: |
389 | - # db.get_xapian_document(item.name, |
390 | - # item.package_name) |
391 | - #except IndexError: |
392 | - # # item is not in the xapian db |
393 | - # pass |
394 | - #else: |
395 | - # # ignore items we already have in the db, ignore |
396 | - # continue |
397 | - # index the item |
398 | - try: |
399 | - parser = SCAPurchasedApplicationParser(item) |
400 | - parser.index_app_info(db_purchased, cache) |
401 | - except: |
402 | - LOG.exception("error processing: %r", item) |
403 | - # add new in memory db to the main db |
404 | - db.add_database(db_purchased) |
405 | - # return a query |
406 | - query = xapian.Query("AH" + PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME) |
407 | - return query |
408 | - |
409 | - |
410 | def update_from_software_center_agent(db, cache, ignore_cache=False, |
411 | include_sca_qa=False): |
412 | """Update the index based on the software-center-agent data.""" |
413 | @@ -1056,31 +1018,60 @@ |
414 | sca.good_data = True |
415 | loop.quit() |
416 | |
417 | + def _available_for_me_cb(sca, available_for_me): |
418 | + LOG.debug("update_from_software_center_agent: available_for_me: %r", |
419 | + available_for_me) |
420 | + sca.available_for_me = available_for_me |
421 | + loop.quit() |
422 | + |
423 | def _error_cb(sca, error): |
424 | LOG.warn("update_from_software_center_agent: error: %r", error) |
425 | - sca.available = [] |
426 | sca.good_data = False |
427 | loop.quit() |
428 | |
429 | - # use the anonymous interface to s-c-agent, scales much better and is |
430 | - # much cache friendlier |
431 | - from softwarecenter.backend.scagent import SoftwareCenterAgent |
432 | - # FIXME: honor ignore_etag here somehow with the new piston based API |
433 | + context = GObject.main_context_default() |
434 | + loop = GObject.MainLoop(context) |
435 | + |
436 | sca = SoftwareCenterAgent(ignore_cache) |
437 | sca.connect("available", _available_cb) |
438 | + sca.connect("available-for-me", _available_for_me_cb) |
439 | sca.connect("error", _error_cb) |
440 | - sca.available = None |
441 | + sca.available = [] |
442 | + sca.available_for_me = [] |
443 | + |
444 | + # query what is available for me first |
445 | + available_for_me_pkgnames = set() |
446 | + # this will ensure we do not trigger a login dialog |
447 | + helper = UbuntuSSO() |
448 | + token = helper.find_oauth_token_sync() |
449 | + if token: |
450 | + sca.query_available_for_me(no_relogin=True) |
451 | + loop.run() |
452 | + for item in sca.available_for_me: |
453 | + try: |
454 | + parser = SCAPurchasedApplicationParser(item) |
455 | + parser.index_app_info(db, cache) |
456 | + available_for_me_pkgnames.add(item.application["package_name"]) |
457 | + except: |
458 | + LOG.exception("error processing: %r", item) |
459 | + |
460 | + # ... now query all that is available |
461 | if include_sca_qa: |
462 | sca.query_available_qa() |
463 | else: |
464 | sca.query_available() |
465 | + |
466 | # create event loop and run it until data is available |
467 | # (the _available_cb and _error_cb will quit it) |
468 | - context = GObject.main_context_default() |
469 | - loop = GObject.MainLoop(context) |
470 | loop.run() |
471 | + |
472 | # process data |
473 | for entry in sca.available: |
474 | + |
475 | + # do not add stuff here thats already purchased to avoid duplication |
476 | + if entry.package_name in available_for_me_pkgnames: |
477 | + continue |
478 | + |
479 | # process events |
480 | while context.pending(): |
481 | context.iteration() |
482 | |
483 | === modified file 'softwarecenter/enums.py' |
484 | --- softwarecenter/enums.py 2012-09-11 00:02:18 +0000 |
485 | +++ softwarecenter/enums.py 2012-09-14 13:44:26 +0000 |
486 | @@ -208,7 +208,6 @@ |
487 | |
488 | |
489 | # fake channels |
490 | -PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME = "for-pay-needs-reinstall" |
491 | AVAILABLE_FOR_PURCHASE_MAGIC_CHANNEL_NAME = "available-for-pay" |
492 | |
493 | |
494 | |
495 | === modified file 'softwarecenter/ui/gtk3/app.py' |
496 | --- softwarecenter/ui/gtk3/app.py 2012-09-11 13:31:09 +0000 |
497 | +++ softwarecenter/ui/gtk3/app.py 2012-09-14 13:44:26 +0000 |
498 | @@ -47,6 +47,8 @@ |
499 | # make pyflakes shut up |
500 | softwarecenter.netstatus.NETWORK_STATE |
501 | |
502 | +from softwarecenter.backend.ubuntusso import UbuntuSSO |
503 | + |
504 | # db imports |
505 | from softwarecenter.db.application import Application |
506 | from softwarecenter.db import ( |
507 | @@ -84,7 +86,10 @@ |
508 | init_sc_css_provider, |
509 | ) |
510 | from softwarecenter.version import VERSION |
511 | -from softwarecenter.db.database import StoreDatabase |
512 | +from softwarecenter.db.database import ( |
513 | + StoreDatabase, |
514 | + get_reinstall_previous_purchases_query, |
515 | + ) |
516 | try: |
517 | from aptd_gtk3 import InstallBackendUI |
518 | InstallBackendUI # pyflakes |
519 | @@ -336,7 +341,7 @@ |
520 | # for use when viewing previous purchases |
521 | self.scagent = None |
522 | self.sso = None |
523 | - self.available_for_me_query = None |
524 | + self.available_for_me_query = get_reinstall_previous_purchases_query() |
525 | |
526 | Gtk.Window.set_default_icon_name("softwarecenter") |
527 | |
528 | @@ -739,25 +744,9 @@ |
529 | channel_manager.feed_in_private_sources_list_entries( |
530 | private_archives) |
531 | |
532 | - def _on_sso_login(self, sso, oauth_result): |
533 | - self._sso_login_successful = True |
534 | - # appmanager needs to know about the oauth token for the reinstall |
535 | - # previous purchases add_license_key call |
536 | - self.app_manager.oauth_token = oauth_result |
537 | - self.scagent.query_available_for_me() |
538 | - |
539 | def _on_style_updated(self, widget, init_css_callback, *args): |
540 | init_css_callback(widget, *args) |
541 | |
542 | - def _available_for_me_result(self, scagent, result_list): |
543 | - #print "available_for_me_result", result_list |
544 | - from softwarecenter.db.update import ( |
545 | - add_from_purchased_but_needs_reinstall_data) |
546 | - available = add_from_purchased_but_needs_reinstall_data(result_list, |
547 | - self.db, self.cache) |
548 | - self.available_for_me_query = available |
549 | - self.available_pane.on_previous_purchases_activated(available) |
550 | - |
551 | def get_icon_filename(self, iconname, iconsize): |
552 | iconinfo = self.icons.lookup_icon(iconname, iconsize, 0) |
553 | if not iconinfo: |
554 | @@ -838,30 +827,53 @@ |
555 | d = LoginDialog(self.glaunchpad, self.datadir, parent=self.window_main) |
556 | d.login() |
557 | |
558 | - def _create_dbus_sso(self): |
559 | - # see bug #773214 for the rationale, do not translate the appname |
560 | - #appname = _("Ubuntu Software Center") |
561 | - appname = SOFTWARE_CENTER_NAME_KEYRING |
562 | - help_text = _("To reinstall previous purchases, sign in to the " |
563 | - "Ubuntu Single Sign-On account you used to pay for them.") |
564 | - #window = self.window_main.get_window() |
565 | - #xid = self.get_window().xid |
566 | - xid = 0 |
567 | - self.sso = get_sso_backend(xid, |
568 | - appname, |
569 | - help_text) |
570 | - self.sso.connect("login-successful", self._on_sso_login) |
571 | - |
572 | - def _login_via_dbus_sso(self): |
573 | - self._create_dbus_sso() |
574 | - self.sso.login() |
575 | - |
576 | - def _create_scagent_if_needed(self): |
577 | - if not self.scagent: |
578 | - from softwarecenter.backend.scagent import SoftwareCenterAgent |
579 | - self.scagent = SoftwareCenterAgent() |
580 | - self.scagent.connect("available-for-me", |
581 | - self._available_for_me_result) |
582 | + def _on_reinstall_purchased_login(self, sso, oauth_result): |
583 | + self._sso_login_successful = True |
584 | + # appmanager needs to know about the oauth token for the reinstall |
585 | + # previous purchases add_license_key call |
586 | + self.app_manager.oauth_token = oauth_result |
587 | + |
588 | + # the software-center-agent will ensure the previous-purchases |
589 | + # get merged in |
590 | + self._run_software_center_agent() |
591 | + |
592 | + # show spinner as this may take some time, the spinner will |
593 | + # automatically go away when a DB refreshes |
594 | + self.available_pane.show_appview_spinner() |
595 | + |
596 | + # show previous purchased |
597 | + self.available_pane.on_previous_purchases_activated( |
598 | + self.available_for_me_query) |
599 | + |
600 | + def on_menuitem_reinstall_purchases_activate(self, menuitem): |
601 | + self.view_manager.set_active_view(ViewPages.AVAILABLE) |
602 | + self.view_manager.search_entry.clear_with_no_signal() |
603 | + # its ok to use the sync version here to get the token, this is |
604 | + # very quick |
605 | + helper = UbuntuSSO() |
606 | + token = helper.find_oauth_token_sync() |
607 | + if token: |
608 | + # trigger a software-center-agent run to ensure the merged in |
609 | + # purchases are fresh |
610 | + self._run_software_center_agent() |
611 | + # we already have the list of available items, so just show it |
612 | + # (no need for spinner here) |
613 | + self.available_pane.on_previous_purchases_activated( |
614 | + self.available_for_me_query) |
615 | + else: |
616 | + # see bug #773214 for the rationale, do not translate the appname |
617 | + #appname = _("Ubuntu Software Center") |
618 | + appname = SOFTWARE_CENTER_NAME_KEYRING |
619 | + help_text = _( |
620 | + "To reinstall previous purchases, sign in to the " |
621 | + "Ubuntu Single Sign-On account you used to pay for them.") |
622 | + #window = self.window_main.get_window() |
623 | + #xid = self.get_window().xid |
624 | + xid = 0 |
625 | + self.sso = get_sso_backend(xid, appname, help_text) |
626 | + self.sso.connect( |
627 | + "login-successful", self._on_reinstall_purchased_login) |
628 | + self.sso.login() |
629 | |
630 | def on_menuitem_recommendations_activate(self, menu_item): |
631 | rec_panel = self.available_pane.cat_view.recommended_for_you_panel |
632 | @@ -875,21 +887,6 @@ |
633 | if res == Gtk.ResponseType.YES: |
634 | rec_panel.opt_in_to_recommendations_service() |
635 | |
636 | - def on_menuitem_reinstall_purchases_activate(self, menuitem): |
637 | - self.view_manager.set_active_view(ViewPages.AVAILABLE) |
638 | - self.view_manager.search_entry.clear_with_no_signal() |
639 | - if self.available_for_me_query: |
640 | - # we already have the list of available items, so just show it |
641 | - # (no need for spinner here) |
642 | - self.available_pane.on_previous_purchases_activated( |
643 | - self.available_for_me_query) |
644 | - else: |
645 | - # show spinner as this may take some time |
646 | - self.available_pane.show_appview_spinner() |
647 | - # fetch the list of available items and show it |
648 | - self._create_scagent_if_needed() |
649 | - self._login_via_dbus_sso() |
650 | - |
651 | def on_menuitem_deauthorize_computer_activate(self, menuitem): |
652 | |
653 | # FIXME: need Ubuntu SSO username here |
654 | |
655 | === modified file 'tests/gtk3/test_purchase.py' |
656 | --- tests/gtk3/test_purchase.py 2012-08-21 08:46:26 +0000 |
657 | +++ tests/gtk3/test_purchase.py 2012-09-14 13:44:26 +0000 |
658 | @@ -71,7 +71,13 @@ |
659 | do_events_with_sleep() |
660 | self.assertTrue(signal_mock.called) |
661 | |
662 | - def test_reinstall_previous_purchase_display(self): |
663 | + |
664 | +class PreviousPurchasesTestCase(unittest.TestCase): |
665 | + |
666 | + @patch("softwarecenter.backend.ubuntusso.UbuntuSSO" |
667 | + ".find_oauth_token_sync") |
668 | + def test_reinstall_previous_purchase_display(self, mock_find_token): |
669 | + mock_find_token.return_value = { 'not': 'important' } |
670 | mock_options = get_mock_options() |
671 | xapiandb = "/var/cache/software-center/" |
672 | app = SoftwareCenterAppGtk3( |
673 | @@ -79,22 +85,19 @@ |
674 | self.addCleanup(app.destroy) |
675 | # real app opens cache async |
676 | app.cache.open() |
677 | - # no real sso |
678 | - with patch.object(app, '_login_via_dbus_sso', |
679 | - lambda: app._available_for_me_result(None, [])): |
680 | - # show it |
681 | - app.window_main.show_all() |
682 | - app.available_pane.init_view() |
683 | + # .. and now pretend we clicked on the menu item |
684 | + app.window_main.show_all() |
685 | + app.available_pane.init_view() |
686 | + do_events_with_sleep() |
687 | + app.on_menuitem_reinstall_purchases_activate(None) |
688 | + # it can take a bit until the sso client is ready |
689 | + for i in range(10): |
690 | + if (app.available_pane.get_current_page() == |
691 | + AvailablePane.Pages.LIST): |
692 | + break |
693 | do_events_with_sleep() |
694 | - app.on_menuitem_reinstall_purchases_activate(None) |
695 | - # it can take a bit until the sso client is ready |
696 | - for i in range(100): |
697 | - if (app.available_pane.get_current_page() == |
698 | - AvailablePane.Pages.LIST): |
699 | - break |
700 | - do_events_with_sleep() |
701 | - self.assertEqual(app.available_pane.get_current_page(), |
702 | - AvailablePane.Pages.LIST) |
703 | + self.assertEqual(app.available_pane.get_current_page(), |
704 | + AvailablePane.Pages.LIST) |
705 | |
706 | |
707 | if __name__ == "__main__": |
708 | |
709 | === modified file 'tests/test_database.py' |
710 | --- tests/test_database.py 2012-09-14 07:31:06 +0000 |
711 | +++ tests/test_database.py 2012-09-14 13:44:26 +0000 |
712 | @@ -166,7 +166,11 @@ |
713 | self.assertTrue(res) |
714 | self.assertEqual(db.get_doccount(), 1) |
715 | |
716 | - def test_build_from_software_center_agent(self): |
717 | + @patch("softwarecenter.backend.ubuntusso.UbuntuSSO" |
718 | + ".find_oauth_token_sync") |
719 | + def test_build_from_software_center_agent(self, mock_find_oauth): |
720 | + # pretend we have no token |
721 | + mock_find_oauth.return_value = None |
722 | db = xapian.inmemory_open() |
723 | cache = apt.Cache() |
724 | # monkey patch distro to ensure we get data |
725 | @@ -365,7 +369,11 @@ |
726 | doc.get_value(value_time) >= last_time |
727 | last_time = doc.get_value(value_time) |
728 | |
729 | - def test_for_purchase_apps_date_published(self): |
730 | + @patch("softwarecenter.backend.ubuntusso.UbuntuSSO" |
731 | + ".find_oauth_token_sync") |
732 | + def test_for_purchase_apps_date_published(self, mock_find_oauth): |
733 | + # pretend we have no token |
734 | + mock_find_oauth.return_value = None |
735 | #os.environ["SOFTWARE_CENTER_DEBUG_HTTP"] = "1" |
736 | #os.environ["SOFTWARE_CENTER_AGENT_HOST"] = "http://sc.staging.ubuntu.com/" |
737 | # staging does not have a valid cert |
738 | |
739 | === modified file 'tests/test_reinstall_purchased.py' |
740 | --- tests/test_reinstall_purchased.py 2012-06-25 17:40:17 +0000 |
741 | +++ tests/test_reinstall_purchased.py 2012-09-14 13:44:26 +0000 |
742 | @@ -1,30 +1,29 @@ |
743 | -import apt_pkg |
744 | -import apt |
745 | import json |
746 | import platform |
747 | -import os |
748 | import unittest |
749 | import xapian |
750 | |
751 | +from gi.repository import GObject |
752 | + |
753 | from mock import patch |
754 | from piston_mini_client import PistonResponseObject |
755 | from tests.utils import ( |
756 | - DATA_DIR, |
757 | + get_test_pkg_info, |
758 | setup_test_env, |
759 | + ObjectWithSignals, |
760 | ) |
761 | setup_test_env() |
762 | |
763 | from softwarecenter.enums import ( |
764 | AppInfoFields, |
765 | AVAILABLE_FOR_PURCHASE_MAGIC_CHANNEL_NAME, |
766 | - PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME, |
767 | XapianValues, |
768 | ) |
769 | -from softwarecenter.db.database import StoreDatabase |
770 | +from softwarecenter.db.database import get_reinstall_previous_purchases_query |
771 | from softwarecenter.db.update import ( |
772 | - add_from_purchased_but_needs_reinstall_data, |
773 | SCAPurchasedApplicationParser, |
774 | SCAApplicationParser, |
775 | + update_from_software_center_agent, |
776 | ) |
777 | |
778 | # Example taken from running: |
779 | @@ -98,62 +97,45 @@ |
780 | "description": "Play DVD-Videos\\r\\n\\r\\nFluendo DVD Player is a software application specially designed to\\r\\nreproduce DVD on Linux/Unix platforms, which provides end users with\\r\\nhigh quality standards.\\r\\n\\r\\nThe following features are provided:\\r\\n* Full DVD Playback\\r\\n* DVD Menu support\\r\\n* Fullscreen support\\r\\n* Dolby Digital pass-through\\r\\n* Dolby Digital 5.1 output and stereo downmixing support\\r\\n* Resume from last position support\\r\\n* Subtitle support\\r\\n* Audio selection support\\r\\n* Multiple Angles support\\r\\n* Support for encrypted discs\\r\\n* Multiregion, works in all regions\\r\\n* Multiple video deinterlacing algorithms", |
781 | "website": null, |
782 | "version": "1.2.1" |
783 | - } |
784 | + }, |
785 | + { |
786 | + "website": "", |
787 | + "package_name": "photobomb", |
788 | + "video_embedded_html_urls": [ ], |
789 | + "demo": null, |
790 | + "keywords": "photos, pictures, editing, gwibber, twitter, facebook, drawing", |
791 | + "video_urls": [ ], |
792 | + "screenshot_url": "http://software-center.ubuntu.com/site_media/screenshots/2011/08/Screenshot-45.png", |
793 | + "id": 83, |
794 | + "archive_id": "commercial-ppa-uploaders/photobomb", |
795 | + "support_url": "http://launchpad.net/photobomb", |
796 | + "icon_url": "http://software-center.ubuntu.com/site_media/icons/2011/08/logo_64.png", |
797 | + "binary_filesize": null, |
798 | + "version": "", |
799 | + "company_name": "", |
800 | + "department": [ |
801 | + "Graphics" |
802 | + ], |
803 | + "tos_url": "", |
804 | + "channel": "For Purchase", |
805 | + "status": "Published", |
806 | + "signing_key_id": "1024R/75254D99", |
807 | + "description": "Easy and Social Image Editor\\nPhotobomb give you easy access to images in your social networking feeds, pictures on your computer and peripherals, and pictures on the web, and let's you draw, write, crop, combine, and generally have a blast mashing 'em all up. Then you can save off your photobomb, or tweet your creation right back to your social network.", |
808 | + "price": "2.99", |
809 | + "debtags": [ ], |
810 | + "date_published": "2011-12-05 18:43:20.794802", |
811 | + "categories": "Graphics", |
812 | + "name": "Photobomb", |
813 | + "license": "GNU GPL v3", |
814 | + "screenshot_urls": [ |
815 | + "http://software-center.ubuntu.com/site_media/screenshots/2011/08/Screenshot-45.png" |
816 | + ], |
817 | + "archive_root": "https://private-ppa.launchpad.net/" |
818 | + } |
819 | ] |
820 | """ |
821 | |
822 | |
823 | -class TestPurchased(unittest.TestCase): |
824 | - """ tests the store database """ |
825 | - |
826 | - def _make_available_for_me_list(self): |
827 | - my_subscriptions = json.loads(SUBSCRIPTIONS_FOR_ME_JSON) |
828 | - return list( |
829 | - PistonResponseObject.from_dict(subs) for subs in my_subscriptions) |
830 | - |
831 | - def setUp(self): |
832 | - # use fixture apt data |
833 | - apt_pkg.config.set("APT::Architecture", "i386") |
834 | - apt_pkg.config.set("Dir::State::status", |
835 | - os.path.join(DATA_DIR, "appdetails", "var", "lib", "dpkg", "status")) |
836 | - # create mocks |
837 | - self.available_to_me = self._make_available_for_me_list() |
838 | - self.cache = apt.Cache() |
839 | - |
840 | - def test_reinstall_purchased_mock(self): |
841 | - # test if the mocks are ok |
842 | - self.assertEqual(len(self.available_to_me), 1) |
843 | - self.assertEqual( |
844 | - self.available_to_me[0].application['package_name'], "photobomb") |
845 | - |
846 | - def test_reinstall_purchased_xapian(self): |
847 | - db = StoreDatabase("/var/cache/software-center/xapian", self.cache) |
848 | - db.open(use_axi=False) |
849 | - # now create purchased debs xapian index (in memory because |
850 | - # we store the repository passwords in here) |
851 | - old_db_len = len(db) |
852 | - query = add_from_purchased_but_needs_reinstall_data( |
853 | - self.available_to_me, db, self.cache) |
854 | - # ensure we have a new item (the available for reinstall one) |
855 | - self.assertEqual(len(db), old_db_len+1) |
856 | - # query |
857 | - enquire = xapian.Enquire(db.xapiandb) |
858 | - enquire.set_query(query) |
859 | - matches = enquire.get_mset(0, len(db)) |
860 | - self.assertEqual(len(matches), 1) |
861 | - distroseries = platform.dist()[2] |
862 | - for m in matches: |
863 | - doc = db.xapiandb.get_document(m.docid) |
864 | - self.assertEqual(doc.get_value(XapianValues.PKGNAME), "photobomb") |
865 | - self.assertEqual( |
866 | - doc.get_value(XapianValues.ARCHIVE_SIGNING_KEY_ID), |
867 | - "1024R/75254D99") |
868 | - self.assertEqual(doc.get_value(XapianValues.ARCHIVE_DEB_LINE), |
869 | - "deb https://username:random3atoken@" |
870 | - "private-ppa.launchpad.net/commercial-ppa-uploaders" |
871 | - "/photobomb/ubuntu %s main" % distroseries) |
872 | - |
873 | - |
874 | class SCAApplicationParserTestCase(unittest.TestCase): |
875 | |
876 | def _make_application_parser(self, piston_application=None): |
877 | @@ -313,12 +295,11 @@ |
878 | for key in (AppInfoFields.LICENSE_KEY, AppInfoFields.LICENSE_KEY_PATH): |
879 | self.assertIsNone(parser.get_value(key)) |
880 | |
881 | - def test_magic_channel(self): |
882 | + def test_purchase_date(self): |
883 | parser = self._make_application_parser() |
884 | - |
885 | self.assertEqual( |
886 | - PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME, |
887 | - parser.get_value(AppInfoFields.CHANNEL)) |
888 | + "2011-09-16 06:37:52", |
889 | + parser.get_value(AppInfoFields.PURCHASED_DATE)) |
890 | |
891 | def test_will_handle_supported_distros_when_available(self): |
892 | # When the fix for bug 917109 reaches production, we will be |
893 | @@ -367,5 +348,73 @@ |
894 | SCAPurchasedApplicationParser.update_debline(orig_debline)) |
895 | |
896 | |
897 | +class TestAvailableForMeMerging(unittest.TestCase): |
898 | + |
899 | + def setUp(self): |
900 | + self.available_for_me = self._make_available_for_me_list() |
901 | + self.available = self._make_available_list() |
902 | + |
903 | + def _make_available_for_me_list(self): |
904 | + my_subscriptions = json.loads(SUBSCRIPTIONS_FOR_ME_JSON) |
905 | + return list( |
906 | + PistonResponseObject.from_dict(subs) for subs in my_subscriptions) |
907 | + |
908 | + def _make_available_list(self): |
909 | + available_apps = json.loads(AVAILABLE_APPS_JSON) |
910 | + return list( |
911 | + PistonResponseObject.from_dict(subs) for subs in available_apps) |
912 | + |
913 | + def _make_fake_scagent(self, available_data, available_for_me_data): |
914 | + sca = ObjectWithSignals() |
915 | + sca.query_available = lambda **kwargs: GObject.timeout_add( |
916 | + 100, lambda: sca.emit('available', sca, available_data)) |
917 | + sca.query_available_for_me = lambda **kwargs: GObject.timeout_add( |
918 | + 100, lambda: sca.emit('available-for-me', |
919 | + sca, available_for_me_data)) |
920 | + return sca |
921 | + |
922 | + def test_reinstall_purchased_mock(self): |
923 | + # test if the mocks are ok |
924 | + self.assertEqual(len(self.available_for_me), 1) |
925 | + self.assertEqual( |
926 | + self.available_for_me[0].application['package_name'], "photobomb") |
927 | + |
928 | + @patch("softwarecenter.db.update.SoftwareCenterAgent") |
929 | + @patch("softwarecenter.db.update.UbuntuSSO") |
930 | + def test_reinstall_purchased_xapian(self, mock_helper, mock_agent): |
931 | + small_available = [ self.available[0] ] |
932 | + mock_agent.return_value = self._make_fake_scagent( |
933 | + small_available, self.available_for_me) |
934 | + |
935 | + db = xapian.inmemory_open() |
936 | + cache = get_test_pkg_info() |
937 | + |
938 | + # now create purchased debs xapian index (in memory because |
939 | + # we store the repository passwords in here) |
940 | + old_db_len = db.get_doccount() |
941 | + update_from_software_center_agent(db, cache) |
942 | + # ensure we have the new item |
943 | + self.assertEqual(db.get_doccount(), old_db_len+2) |
944 | + # query |
945 | + query = get_reinstall_previous_purchases_query() |
946 | + enquire = xapian.Enquire(db) |
947 | + enquire.set_query(query) |
948 | + matches = enquire.get_mset(0, db.get_doccount()) |
949 | + self.assertEqual(len(matches), 1) |
950 | + distroseries = platform.dist()[2] |
951 | + for m in matches: |
952 | + doc = db.get_document(m.docid) |
953 | + self.assertEqual(doc.get_value(XapianValues.PKGNAME), "photobomb") |
954 | + self.assertEqual( |
955 | + doc.get_value(XapianValues.ARCHIVE_SIGNING_KEY_ID), |
956 | + "1024R/75254D99") |
957 | + self.assertEqual(doc.get_value(XapianValues.ARCHIVE_DEB_LINE), |
958 | + "deb https://username:random3atoken@" |
959 | + "private-ppa.launchpad.net/commercial-ppa-uploaders" |
960 | + "/photobomb/ubuntu %s main" % distroseries) |
961 | + |
962 | + |
963 | if __name__ == "__main__": |
964 | + import logging |
965 | + logging.basicConfig(level=logging.DEBUG) |
966 | unittest.main() |
967 | |
968 | === modified file 'tests/test_ubuntu_sso_api.py' |
969 | --- tests/test_ubuntu_sso_api.py 2012-05-30 18:39:55 +0000 |
970 | +++ tests/test_ubuntu_sso_api.py 2012-09-14 13:44:26 +0000 |
971 | @@ -6,7 +6,7 @@ |
972 | ) |
973 | setup_test_env() |
974 | from softwarecenter.backend.ubuntusso import (UbuntuSSOAPIFake, |
975 | - UbuntuSSOAPI, |
976 | + UbuntuSSO, |
977 | get_ubuntu_sso_backend, |
978 | ) |
979 | |
980 | @@ -15,7 +15,7 @@ |
981 | |
982 | def test_fake_and_real_provide_similar_methods(self): |
983 | """ test if the real and fake sso provide the same functions """ |
984 | - sso_real = UbuntuSSOAPI |
985 | + sso_real = UbuntuSSO |
986 | sso_fake = UbuntuSSOAPIFake |
987 | # ensure that both fake and real implement the same methods |
988 | self.assertEqual( |
989 | @@ -25,7 +25,7 @@ |
990 | def test_get_ubuntu_backend(self): |
991 | # test that we get the real one |
992 | self.assertEqual(type(get_ubuntu_sso_backend()), |
993 | - UbuntuSSOAPI) |
994 | + UbuntuSSO) |
995 | # test that we get the fake one |
996 | os.environ["SOFTWARE_CENTER_FAKE_REVIEW_API"] = "1" |
997 | self.assertEqual(type(get_ubuntu_sso_backend()), |
998 | |
999 | === modified file 'utils/piston-helpers/piston_generic_helper.py' |
1000 | --- utils/piston-helpers/piston_generic_helper.py 2012-08-15 08:36:50 +0000 |
1001 | +++ utils/piston-helpers/piston_generic_helper.py 2012-09-14 13:44:26 +0000 |
1002 | @@ -25,7 +25,6 @@ |
1003 | import pickle |
1004 | import sys |
1005 | |
1006 | -from gi.repository import GObject |
1007 | |
1008 | # useful for debugging |
1009 | if "SOFTWARE_CENTER_DEBUG_HTTP" in os.environ: |
1010 | @@ -45,13 +44,8 @@ |
1011 | |
1012 | import softwarecenter.paths |
1013 | from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR |
1014 | -from softwarecenter.backend.login_sso import get_sso_backend |
1015 | |
1016 | -from softwarecenter.enums import (SOFTWARE_CENTER_NAME_KEYRING, |
1017 | - SOFTWARE_CENTER_SSO_DESCRIPTION, |
1018 | - ) |
1019 | - |
1020 | -from softwarecenter.utils import clear_token_from_ubuntu_sso_sync |
1021 | +from softwarecenter.backend.ubuntusso import UbuntuSSO |
1022 | |
1023 | # the piston import |
1024 | from softwarecenter.backend.piston.ubuntusso_pristine import UbuntuSsoAPI |
1025 | @@ -60,90 +54,23 @@ |
1026 | from softwarecenter.backend.piston.sreclient_pristine import ( |
1027 | SoftwareCenterRecommenderAPI) |
1028 | |
1029 | + |
1030 | +from softwarecenter.enums import RECOMMENDER_HOST |
1031 | +SoftwareCenterRecommenderAPI.default_service_root = \ |
1032 | + RECOMMENDER_HOST + "/api/1.0" |
1033 | + |
1034 | + |
1035 | # patch default_service_root to the one we use |
1036 | from softwarecenter.enums import UBUNTU_SSO_SERVICE |
1037 | # *Don't* append /api/1.0, as it's already included in UBUNTU_SSO_SERVICE |
1038 | UbuntuSsoAPI.default_service_root = UBUNTU_SSO_SERVICE |
1039 | |
1040 | -from softwarecenter.enums import RECOMMENDER_HOST |
1041 | -SoftwareCenterRecommenderAPI.default_service_root = \ |
1042 | - RECOMMENDER_HOST + "/api/1.0" |
1043 | - |
1044 | |
1045 | RatingsAndReviewsAPI # pyflakes |
1046 | UbuntuSsoAPI # pyflakes |
1047 | SoftwareCenterAgentAPI # pyflakes |
1048 | SoftwareCenterRecommenderAPI # pyflakes |
1049 | |
1050 | -from gettext import gettext as _ |
1051 | - |
1052 | - |
1053 | -# helper that is only used to verify that the token is ok |
1054 | -# and trigger cleanup if not |
1055 | -class SSOLoginHelper(object): |
1056 | - |
1057 | - def __init__(self, xid=0): |
1058 | - self.oauth = None |
1059 | - self.xid = xid |
1060 | - self.loop = GObject.MainLoop(GObject.main_context_default()) |
1061 | - |
1062 | - def _login_successful(self, sso_backend, oauth_result): |
1063 | - LOG.debug("_login_successful") |
1064 | - self.oauth = oauth_result |
1065 | - # FIXME: actually verify the token against ubuntu SSO |
1066 | - self.loop.quit() |
1067 | - |
1068 | - def verify_token_sync(self, token): |
1069 | - """ Verify that the token is valid |
1070 | - |
1071 | - Note that this may raise httplib2 exceptions if the server |
1072 | - is not reachable |
1073 | - """ |
1074 | - LOG.debug("verify_token") |
1075 | - auth = piston_mini_client.auth.OAuthAuthorizer( |
1076 | - token["token"], token["token_secret"], |
1077 | - token["consumer_key"], token["consumer_secret"]) |
1078 | - api = UbuntuSsoAPI(auth=auth) |
1079 | - try: |
1080 | - res = api.whoami() |
1081 | - except piston_mini_client.failhandlers.APIError as e: |
1082 | - LOG.exception("api.whoami failed with APIError: '%s'" % e) |
1083 | - return False |
1084 | - return len(res) > 0 |
1085 | - |
1086 | - def clear_token(self): |
1087 | - clear_token_from_ubuntu_sso_sync(SOFTWARE_CENTER_NAME_KEYRING) |
1088 | - |
1089 | - def get_oauth_token_sync(self): |
1090 | - self.oauth = None |
1091 | - sso = get_sso_backend( |
1092 | - self.xid, |
1093 | - SOFTWARE_CENTER_NAME_KEYRING, |
1094 | - _(SOFTWARE_CENTER_SSO_DESCRIPTION)) |
1095 | - sso.connect("login-successful", self._login_successful) |
1096 | - sso.connect("login-failed", lambda s: self.loop.quit()) |
1097 | - sso.connect("login-canceled", lambda s: self.loop.quit()) |
1098 | - sso.login_or_register() |
1099 | - self.loop.run() |
1100 | - return self.oauth |
1101 | - |
1102 | - def get_oauth_token_and_verify_sync(self): |
1103 | - token = self.get_oauth_token_sync() |
1104 | - # check if the token is valid and reset it if it is not |
1105 | - if token: |
1106 | - # verify token will return false if there is a API error, |
1107 | - # but there maybe httplib2 errors if there is no network, |
1108 | - # so ignore them |
1109 | - try: |
1110 | - if not self.verify_token_sync(token): |
1111 | - self.clear_token() |
1112 | - # re-trigger login once |
1113 | - token = self.get_oauth_token_sync() |
1114 | - except Exception as e: |
1115 | - LOG.warn( |
1116 | - "token could not be verified (network problem?): %s" % e) |
1117 | - return token |
1118 | - |
1119 | |
1120 | LOG = logging.getLogger(__name__) |
1121 | |
1122 | @@ -164,6 +91,8 @@ |
1123 | help="force disable offline mode") |
1124 | parser.add_argument("--needs-auth", default=False, action="store_true", |
1125 | help="need oauth credentials") |
1126 | + parser.add_argument("--no-relogin", default=False, action="store_true", |
1127 | + help="do not attempt relogin if token is invalid") |
1128 | parser.add_argument("--output", default="pickle", |
1129 | help="output result as [pickle|json|text]") |
1130 | parser.add_argument("--parent-xid", default=0, |
1131 | @@ -191,8 +120,9 @@ |
1132 | softwarecenter.paths.datadir = args.datadir |
1133 | |
1134 | if args.needs_auth: |
1135 | - helper = SSOLoginHelper(args.parent_xid) |
1136 | - token = helper.get_oauth_token_and_verify_sync() |
1137 | + helper = UbuntuSSO(args.parent_xid) |
1138 | + token = helper.get_oauth_token_and_verify_sync( |
1139 | + no_relogin=args.no_relogin) |
1140 | # if we don't have a token, error here |
1141 | if not token: |
1142 | # it may happen that the parent is closed already so the pipe |
1143 | |
1144 | === modified file 'utils/update-software-center-agent' |
1145 | --- utils/update-software-center-agent 2012-03-16 11:09:25 +0000 |
1146 | +++ utils/update-software-center-agent 2012-09-14 13:44:26 +0000 |
1147 | @@ -96,6 +96,10 @@ |
1148 | "a write lock on %s" % pathname) |
1149 | sys.exit(1) |
1150 | |
1151 | + # ensure permissions are 0700 as this may contain repo passwords from |
1152 | + # the reinstall-previous-purchase repos |
1153 | + os.chmod(pathname, 0o700) |
1154 | + |
1155 | # the following requires a http connection, so we do it in a |
1156 | # seperate database |
1157 | include_sca_qa = "SOFTWARE_CENTER_AGENT_INCLUDE_QA" in os.environ |
Careful review appreciated, should be good, but there is quite a bit of churn.