Merge lp:~mvo/software-center/oauth-from-webkit into lp:software-center

Proposed by Michael Vogt
Status: Work in progress
Proposed branch: lp:~mvo/software-center/oauth-from-webkit
Merge into: lp:software-center
Diff against target: 249 lines (+142/-8) (has conflicts)
4 files modified
softwarecenter/backend/login_sso.py (+31/-2)
softwarecenter/ui/gtk3/views/purchaseview.py (+11/-2)
softwarecenter/ui/gtk3/views/webkit.py (+46/-4)
tests/gtk3/test_webkit.py (+54/-0)
Text conflict in softwarecenter/ui/gtk3/views/webkit.py
To merge this branch: bzr merge lp:~mvo/software-center/oauth-from-webkit
Reviewer Review Type Date Requested Status
Anthony Lenton Pending
software-store-developers Pending
Review via email: mp+113169@code.launchpad.net

Description of the change

This adds support for having oauth signed requests from the webkit view to avoid having to login again into the web login if there is a oauth token in the keyring already.

To post a comment you must log in.
Revision history for this message
Michael Vogt (mvo) wrote :

Setting to work in progress until Anthony gets a chance to look at it.

Revision history for this message
Michael Vogt (mvo) wrote :

To test you can run:
$ PYTHONPATH=. python softwarecenter/ui/gtk3/views/purchaseview.py https://software-center.ubuntu.com/api/2.0/subscriptions

This probably needs a bit of cleanup but the basic principal should be there.

Unmerged revisions

3053. By Michael Vogt

add support for oauth signing into our webkit to allow avoiding the first (web) login if there is a auth token already

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'softwarecenter/backend/login_sso.py'
2--- softwarecenter/backend/login_sso.py 2012-04-16 20:51:09 +0000
3+++ softwarecenter/backend/login_sso.py 2012-07-03 08:41:20 +0000
4@@ -25,6 +25,8 @@
5 import string
6 import os
7
8+from gi.repository import GObject
9+
10 from dbus.mainloop.glib import DBusGMainLoop
11 DBusGMainLoop(set_as_default=True)
12
13@@ -36,6 +38,11 @@
14
15 LOG = logging.getLogger(__name__)
16
17+from ubuntu_sso import (
18+ DBUS_BUS_NAME,
19+ DBUS_CREDENTIALS_IFACE,
20+ DBUS_CREDENTIALS_PATH,
21+ )
22
23 class LoginBackendDbusSSO(LoginBackend):
24
25@@ -44,8 +51,11 @@
26 self.appname = appname
27 self.help_text = help_text
28 self.bus = dbus.SessionBus()
29- self.proxy = self.bus.get_object(
30- 'com.ubuntu.sso', '/com/ubuntu/sso/credentials')
31+ obj = self.bus.get_object(bus_name=DBUS_BUS_NAME,
32+ object_path=DBUS_CREDENTIALS_PATH,
33+ follow_name_owner_changes=True)
34+ self.proxy = dbus.Interface(object=obj,
35+ dbus_interface=DBUS_CREDENTIALS_IFACE)
36 self.proxy.connect_to_signal("CredentialsFound",
37 self._on_credentials_found)
38 self.proxy.connect_to_signal("CredentialsError",
39@@ -73,6 +83,25 @@
40 self._credentials = None
41 self.proxy.register(self.appname, self._get_params())
42
43+ def find_credentials_sync(self):
44+ LOG.debug("find_credentials_sync()")
45+ self._credentials = None
46+
47+ # setup tmp handlers that stop the loop
48+ loop = GObject.MainLoop(GObject.main_context_default())
49+ sigs = []
50+ sigs.append(self.connect("login-successful", lambda s,c: loop.quit()))
51+ sigs.append(self.connect("login-failed", lambda s: loop.quit()))
52+
53+ # run and wait for the handlers to exit the loop
54+ self.proxy.find_credentials(self.appname, self._get_params())
55+ loop.run()
56+
57+ for sig in sigs:
58+ self.disconnect(sig)
59+
60+ return self._credentials
61+
62 def _on_credentials_found(self, app_name, credentials):
63 LOG.debug("_on_credentials_found for '%s'" % app_name)
64 if app_name != self.appname:
65
66=== modified file 'softwarecenter/ui/gtk3/views/purchaseview.py'
67--- softwarecenter/ui/gtk3/views/purchaseview.py 2012-05-30 18:39:55 +0000
68+++ softwarecenter/ui/gtk3/views/purchaseview.py 2012-07-03 08:41:20 +0000
69@@ -36,6 +36,12 @@
70 from softwarecenter.ui.gtk3.utils import get_parent
71 from softwarecenter.ui.gtk3.views.webkit import ScrolledWebkitWindow
72
73+from softwarecenter.backend.login_sso import get_sso_backend
74+from softwarecenter.enums import (SOFTWARE_CENTER_NAME_KEYRING,
75+ SOFTWARE_CENTER_SSO_DESCRIPTION,
76+ )
77+
78+
79 LOG = logging.getLogger(__name__)
80
81
82@@ -104,7 +110,10 @@
83 GObject.GObject.__init__(self)
84 self.wk = None
85 self._wk_handlers_blocked = False
86- self._oauth_token = None
87+ # for the oauth
88+ sso = get_sso_backend(None, SOFTWARE_CENTER_NAME_KEYRING, "")
89+ self._oauth_token = sso.find_credentials_sync()
90+
91 self.config = get_config()
92
93 def _log_debug_output(self, *args):
94@@ -112,7 +121,7 @@
95
96 def init_view(self):
97 if self.wk is None:
98- self.wk = ScrolledWebkitWindow()
99+ self.wk = ScrolledWebkitWindow(self._oauth_token)
100 #self.wk.webkit.connect("new-window-policy-decision-requested",
101 # self._on_new_window)
102 self.wk.webkit.connect("create-web-view", self._on_create_web_view)
103
104=== modified file 'softwarecenter/ui/gtk3/views/webkit.py'
105--- softwarecenter/ui/gtk3/views/webkit.py 2012-06-28 07:20:49 +0000
106+++ softwarecenter/ui/gtk3/views/webkit.py 2012-07-03 08:41:20 +0000
107@@ -17,8 +17,16 @@
108 # this program; if not, write to the Free Software Foundation, Inc.,
109 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
110
111+<<<<<<< TREE
112 import os
113
114+=======
115+import urlparse
116+
117+from oauth.oauth import (
118+ OAuthRequest, OAuthToken, OAuthConsumer, OAuthSignatureMethod_PLAINTEXT)
119+
120+>>>>>>> MERGE-SOURCE
121 from gi.repository import WebKit as webkit
122 from gi.repository import Gtk
123 from gi.repository import Pango
124@@ -44,14 +52,17 @@
125 session.add_feature(cookie_jar)
126 global_webkit_init()
127
128-
129 class LocaleAwareWebView(webkit.WebView):
130
131- def __init__(self):
132+ SIGN_WITH_OAUTH_URL = "https://software-center.ubuntu.com"
133+ OAUTH_REALM = "software-center.ubuntu.com"
134+
135+ def __init__(self, oauth_token=None):
136 # actual webkit init
137 webkit.WebView.__init__(self)
138 self.connect("resource-request-starting",
139 self._on_resource_request_starting)
140+ self._oauth = oauth_token
141
142 def _on_resource_request_starting(self, view, frame, res, req, resp):
143 lang = get_language()
144@@ -64,13 +75,44 @@
145 # print name, value
146 #headers.foreach(_show_header, None)
147
148+ # SEE FULL HTTP DEBUG WITH:
149+ # $ SOFTWARE_CENTER_DEBUG_WEBKIT=1 ./software-center
150+ # once lp:~mvo/software-center/webkit-http-debug is merged
151+
152+ # sign with oauth if we have the right url
153+ url = req.get_property("uri")
154+ if url.startswith(self.SIGN_WITH_OAUTH_URL):
155+ self._sign_with_oauth(req)
156+
157+ def _sign_with_oauth(self, req):
158+ """ OAuth sign a SoupRequest """
159+ message = req.get_message()
160+ if message and self._oauth:
161+ consumer = OAuthConsumer(
162+ self._oauth["consumer_key"], self._oauth["consumer_secret"])
163+ token = OAuthToken(
164+ self._oauth["token"], self._oauth["token_secret"])
165+ url = req.get_property("uri")
166+ parts = urlparse.urlparse(url)
167+ parameters = urlparse.parse_qs(parts.query)
168+ oauth_request = OAuthRequest.from_consumer_and_token(
169+ consumer, token, http_url=url, parameters=parameters)
170+ oauth_request.sign_request(
171+ OAuthSignatureMethod_PLAINTEXT(), consumer, token)
172+ # FIXME: what do we need here?
173+ oauth_realm = self.OAUTH_REALM
174+ # update the headers
175+ headers = message.get_property("request-headers")
176+ for k,v in oauth_request.to_header(realm=oauth_realm).iteritems():
177+ headers.append(k, v)
178+
179
180 class ScrolledWebkitWindow(Gtk.VBox):
181
182- def __init__(self, include_progress_ui=False):
183+ def __init__(self, oauth_token=None, include_progress_ui=False):
184 super(ScrolledWebkitWindow, self).__init__()
185 # get webkit
186- self.webkit = LocaleAwareWebView()
187+ self.webkit = LocaleAwareWebView(oauth_token)
188 settings = self.webkit.get_settings()
189 settings.set_property("enable-plugins", False)
190 # add progress UI if needed
191
192=== added file 'tests/gtk3/test_webkit.py'
193--- tests/gtk3/test_webkit.py 1970-01-01 00:00:00 +0000
194+++ tests/gtk3/test_webkit.py 2012-07-03 08:41:20 +0000
195@@ -0,0 +1,54 @@
196+import os
197+import unittest
198+
199+from gi.repository import GObject
200+from gi.repository import WebKit
201+from gi.repository import Soup
202+
203+from testutils import setup_test_env
204+setup_test_env()
205+
206+from softwarecenter.ui.gtk3.views.webkit import LocaleAwareWebView
207+
208+class WebkitTestCase(unittest.TestCase):
209+
210+ def setUp(self):
211+ self.loop = GObject.MainLoop(GObject.main_context_default())
212+ # add log
213+ session = WebKit.get_default_session()
214+ logger = Soup.Logger.new(Soup.LoggerLogLevel.HEADERS, -1)
215+ logger.set_printer(self._soup_log_collector, None)
216+ logger.attach(session)
217+ self.addCleanup(lambda: logger.detach(session))
218+ self._soup_headers = []
219+
220+ def test_webkit_oauth(self):
221+ oauth = { "token" : "aToken",
222+ "consumer_key" : "aConsumerKey",
223+ "consumer_secret" : "aConsumerSecret",
224+ "token_secret" : "aTokenSecret",
225+ }
226+ wk = LocaleAwareWebView(oauth_token=oauth)
227+ wk.connect("notify::load-status", self._on_load_status_changed)
228+ wk.open("https://software-center.ubuntu.com/subscriptions/en/ubuntu/precise/+new/?archive_id=commercial-ppa-uploaders%2Fworld-of-goo&arch=amd64")
229+ self.loop.run()
230+ # poor mans check
231+ for header in self._soup_headers:
232+ if "oauth_signature" in header:
233+ break
234+ else:
235+ raise Exception("Failed to find oauth_signature in headers")
236+
237+ def _soup_log_collector(self, logger, level, direction, data, user_data):
238+ #print logger, level, direction, data
239+ self._soup_headers.append(data)
240+
241+ def _on_load_status_changed(self, view, pspec):
242+ prop = pspec.name
243+ status = view.get_property(prop)
244+ if (status == WebKit.LoadStatus.FINISHED or
245+ status == WebKit.LoadStatus.FAILED):
246+ self.loop.quit()
247+
248+if __name__ == "__main__":
249+ unittest.main()

Subscribers

People subscribed via source and target branches