Merge lp:~gary-wzl77/account-plugins/mcloud-plugin-lp1587282 into lp:account-plugins

Proposed by Gary.Wang
Status: Merged
Merged at revision: 171
Proposed branch: lp:~gary-wzl77/account-plugins/mcloud-plugin-lp1587282
Merge into: lp:account-plugins
Prerequisite: lp:~mardy/account-plugins/owncloud-1570986
Diff against target: 594 lines (+493/-0)
11 files modified
.bzrignore (+1/-0)
Makefile.am (+2/-0)
configure.ac (+15/-0)
data/providers/mcloud.provider.in.in (+29/-0)
debian/account-plugin-mcloud.install (+4/-0)
debian/control (+8/-0)
debian/rules (+2/-0)
qml/mcloud/ErrorItem.qml (+48/-0)
qml/mcloud/Main.qml (+7/-0)
qml/mcloud/OAuth.qml (+283/-0)
qml/mcloud/WebView.qml (+94/-0)
To merge this branch: bzr merge lp:~gary-wzl77/account-plugins/mcloud-plugin-lp1587282
Reviewer Review Type Date Requested Status
Alberto Mardegan (community) Needs Information
Review via email: mp+296088@code.launchpad.net

This proposal supersedes a proposal from 2016-05-31.

Commit message

Add mcloud plugin.

Description of the change

Add mcloud plugin

Testing instructions:
1) manually install the account-plugin-mcloud package
2) download and install mcloud scope package from
   https://drive.google.com/open?id=0B2H9ECPSSfqIcGtmUEdZNHpVNVU
3) launch mcloud scope and click "Login to MCloud"

To post a comment you must log in.
Revision history for this message
Alberto Mardegan (mardy) wrote :

Hi Gary, thanks for this plugin!
Do you have a link to the OAuth documentation for mccloud? Better if in English :-)

I don't believe that mccould OAuth implementation is special; what you have coded as WORKAROUND 1 & 2 seem to be the normal web flow. I think you can remove those two workarounds, and just change the .provider file by replacing "user_agent" with "web_server", then add your ClientSecret in there too, and it should work.

Once you get it to work, can you also try removing the "DisableStateParameter" option, and see if it continues working? It's preferable not to use that option, because it disables a security feature.

As for the user agent, could you please file a bug against https://launchpad.net/ubuntu/+source/webbrowser-app and ask for a user-agent override to be added in Ubuntu.WebView?

If we manage to get all the above done, then we won't need any workarounds in the plugin, and we can avoid duplicating the code here. :-)

review: Needs Information
Revision history for this message
Gary.Wang (gary-wzl77) wrote :

Thanks for your hints. mandy
 You can find the mcloud OAuth doc here,
 http://caiyun.feixin.10086.cn/Mcloud/dev/
 sorry it's written in Chinese. I guess google translate can help on this :)

  Also I tried to modify the provider file according to your tips
  1. web_server mechanism (with DisableStateParameter removed )
       server popups error page

       however If we specify DisableStateParameter in provider
       log:
       browser-request.cpp 123 ~BrowserRequestPrivate
       browser-request.cpp 323 closeView
       qml: Authentication error, code 1
       qml: Removing credentials...

       AccountService.NoAccountError. That's because session data returned from server doesn't contains user account information.

   2. user_agent mechanism
      log:
       bowser-request.cpp 123 ~BrowserRequestPrivate
       browser-request.cpp 323 closeView
       qml: Authentication error, code 3
       qml: Removing credentials...
       qml: ====== PLUGIN FINISHED ======

     Root cause is the url format that cmcc server give us back is not correct when it navigates to our redirect url with authorized code.
     http://developer.ubuntu.com/en/?code=7598B08A870A5.....
     The correct one should be like as following
     http://developer.ubuntu.com/en/#code=7598B08A870A5.....

     Error occurs when authentication flow runs here
     https://gitlab.com/accounts-sso/signon-plugin-oauth2/blob/master/src/oauth2plugin.cpp#L355

     That's why I apply workaround1 & 2 in this mechanism to create fake "access token".
     Actually, we can check if url contains '?' in this step.
     But to be honest, I don't like this "fix" since redirection url format is incompliant with OAuth2.0 specs.

    I create a test scope to reproduce above issues, Please take a look.
    https://code.launchpad.net/~gary-wzl77/+junk/maccount

   Thanks.

Revision history for this message
Alberto Mardegan (mardy) wrote :

On 01/06/2016 14:48, Gary.Wang wrote:
> Also I tried to modify the provider file according to your tips
> 1. web_server mechanism (with DisableStateParameter removed )
> server popups error page
>
> however If we specify DisableStateParameter in provider
> log:
> browser-request.cpp 123 ~BrowserRequestPrivate
> browser-request.cpp 323 closeView
> qml: Authentication error, code 1
> qml: Removing credentials...
>
> AccountService.NoAccountError. That's because session data returned from server doesn't contains user account information.

Can you please run the following command

  echo "LoggingLevel=2" > ~/.config/signond.conf

and then try again with the web_server method, and show me the contents
of the syslog? (in a pastebin)

I pushed my changes at lp:~mardy/account-plugins/mcloud-plugin-lp1587282
but I'm unable to test them because I don't know how to login. :-(
I managed to create an account in cmpassport.com, but I don't know what
I should enter in the login screen here. Can you help me?

Revision history for this message
Gary.Wang (gary-wzl77) wrote :

Sorry, I think I would give your the test account in advance. I sent you via email. Please have a check.

I attach the link for syslog in pastebin
https://pastebin.canonical.com/157865/

Here is the log after running online-accounts-service
https://pastebin.canonical.com/157864/

Please let me know if there is anything I can do for you.
Thanks.

Revision history for this message
Alberto Mardegan (mardy) wrote :

Hi Gary, good news! :-)

Thanks to your help with the account, after some attempts I got the plugin working. My code is in lp:~mardy/account-plugins/mcloud-plugin-lp1587282 (I'll prepare a merge proposal of my branch into yours).
Unfortunately it requires a little change in the signon-plugin-oauth (https://gitlab.com/accounts-sso/signon-plugin-oauth2/commit/e812eb3e76a1221074e3cf27208aacced5091e42) but I'll take care of landing that one.

I didn't have any problem because of the UserAgent, but I was testing on my desktop, so maybe on the phone it's different.

Revision history for this message
Gary.Wang (gary-wzl77) wrote :

Hi mandy
     That's great. Please have a check if it works on the phone.
     Thanks so much for your help on this. :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2016-05-31 09:02:02 +0000
3+++ .bzrignore 2016-05-31 09:02:02 +0000
4@@ -28,6 +28,7 @@
5 /debian/account-plugin-google/
6 /debian/account-plugin-icons/
7 /debian/account-plugin-identica/
8+/debian/account-plugin-mcloud/
9 /debian/account-plugin-owncloud/
10 /debian/account-plugin-sina/
11 /debian/account-plugin-sohu/
12
13=== modified file 'Makefile.am'
14--- Makefile.am 2016-05-31 09:02:02 +0000
15+++ Makefile.am 2016-05-31 09:02:02 +0000
16@@ -89,6 +89,7 @@
17 data/providers/identica.provider.in.in \
18 data/providers/linkedin.provider.in.in \
19 data/providers/instagram.provider.in.in \
20+ data/providers/mcloud.provider.in.in \
21 data/providers/owncloud.provider.in.in \
22 data/providers/sina.provider.in.in \
23 data/providers/sohu.provider.in.in \
24@@ -139,6 +140,7 @@
25 # https://bugs.launchpad.net/bugs/1571587
26 iconsdir = $(datadir)/icons/hicolor/32x32/apps
27 dist_icons_DATA = \
28+ data/icons/mcloud.png \
29 data/icons/owncloud.png \
30 data/icons/vk.png
31
32
33=== modified file 'configure.ac'
34--- configure.ac 2016-05-31 09:02:02 +0000
35+++ configure.ac 2016-05-31 09:02:02 +0000
36@@ -228,6 +228,20 @@
37 AC_SUBST(SOHU_CLIENT_ID, ["$sohu_client_id"])
38 AC_SUBST(SOHU_CLIENT_SECRET, ["$sohu_client_secret"])
39
40+# Set Mcloud client id/secret
41+AC_ARG_WITH(mcloud-client-id,
42+ [AS_HELP_STRING([--with-mcloud-client-id],
43+ [Mcloud client id])],
44+ [mcloud_client_id=$withval],
45+ [mcloud_client_id="APP1ZtqoN3R0002"])
46+AC_ARG_WITH(mcloud-client-secret,
47+ [AS_HELP_STRING([--with-mcloud-client-secret],
48+ [Mcloud client secret])],
49+ [mcloud_client_secret=$withval],
50+ [mcloud_client_secret="A70EFCDC91456349E7FDECF0A33574AC"])
51+AC_SUBST(MCLOUD_CLIENT_ID, ["$mcloud_client_id"])
52+AC_SUBST(MCLOUD_CLIENT_SECRET, ["$mcloud_client_secret"])
53+
54 # Set VK client id
55 AC_ARG_WITH(vk-client-id,
56 [AS_HELP_STRING([--with-vk-client-id],
57@@ -252,6 +266,7 @@
58 data/providers/identica.provider.in
59 data/providers/linkedin.provider.in
60 data/providers/instagram.provider.in
61+ data/providers/mcloud.provider.in
62 data/providers/owncloud.provider.in
63 data/providers/sina.provider.in
64 data/providers/sohu.provider.in
65
66=== added file 'data/icons/mcloud.png'
67Binary files data/icons/mcloud.png 1970-01-01 00:00:00 +0000 and data/icons/mcloud.png 2016-05-31 09:02:02 +0000 differ
68=== added file 'data/providers/mcloud.provider.in.in'
69--- data/providers/mcloud.provider.in.in 1970-01-01 00:00:00 +0000
70+++ data/providers/mcloud.provider.in.in 2016-05-31 09:02:02 +0000
71@@ -0,0 +1,29 @@
72+<?xml version="1.0" encoding="UTF-8"?>
73+<provider id="mcloud">
74+ <name>MCloud</name>
75+ <icon>mcloud</icon>
76+ <translations>MCloud</translations>
77+ <domains>.*10086\.cn</domains>
78+ <plugin>generic-oauth</plugin>
79+ <single-account>true</single-account>
80+ <template>
81+ <group name="auth">
82+ <setting name="method">oauth2</setting>
83+ <setting name="mechanism">user_agent</setting>
84+ <group name="oauth2">
85+ <group name="user_agent">
86+ <setting name="Host">caiyun.feixin.10086.cn</setting>
87+ <setting name="AuthPath">authorize.jsp</setting>
88+ <setting name="TokenPath">access_token</setting>
89+ <setting name="RedirectUri">http://developer.ubuntu.com/en/</setting>
90+ <setting name="ResponseType">code</setting>
91+ <setting name="ClientId">@MCLOUD_CLIENT_ID@</setting>
92+ <setting type="as" name="Scope">['nd_cloud']</setting>
93+ <setting name="ForceClientAuthViaRequestBody" type="b">true</setting>
94+ <setting name="DisableStateParameter" type="b">true</setting>
95+ <setting type="as" name="AllowedSchemes">['https', 'http']</setting>
96+ </group>
97+ </group>
98+ </group>
99+ </template>
100+</provider>
101
102=== added file 'debian/account-plugin-mcloud.install'
103--- debian/account-plugin-mcloud.install 1970-01-01 00:00:00 +0000
104+++ debian/account-plugin-mcloud.install 2016-05-31 09:02:02 +0000
105@@ -0,0 +1,4 @@
106+usr/share/accounts/providers/mcloud.provider
107+usr/share/accounts/qml-plugins/mcloud/*.qml
108+usr/share/icons/hicolor/32x32/apps/mcloud.png
109+
110
111=== modified file 'debian/control'
112--- debian/control 2016-05-31 09:02:02 +0000
113+++ debian/control 2016-05-31 09:02:02 +0000
114@@ -144,6 +144,14 @@
115 Description: GNOME Control Center account plugin for single signon - Instagram
116 GNOME Control Center account plugins for single signon
117
118+Package: account-plugin-mcloud
119+Architecture: all
120+Depends: ${misc:Depends},
121+ ubuntu-system-settings-online-accounts,
122+Description: Account plugin for online accounts - mCloud
123+ Online Accounts plugin provide the user interface to create accounts for
124+ third party services.
125+
126 Package: account-plugin-owncloud
127 Architecture: all
128 Depends: ${misc:Depends},
129
130=== modified file 'debian/rules'
131--- debian/rules 2016-05-31 09:02:02 +0000
132+++ debian/rules 2016-05-31 09:02:02 +0000
133@@ -22,6 +22,8 @@
134 --with-linkedin-consumer-secret="BazRki2LE8eZtcqh" \
135 --with-instagram-client-id="01c3df41a2274a14882adea8e8ebbd46" \
136 --with-instagram-client-secret="4751ccdc39c648719ea83cfb1c866c26" \
137+ --with-mcloud-client-id="APP1ZtqoN3R0002" \
138+ --with-mcloud-client-secret="A70EFCDC91456349E7FDECF0A33574AC" \
139 --with-vk-client-id="5402699"
140
141 override_dh_install:
142
143=== added directory 'qml/mcloud'
144=== added file 'qml/mcloud/ErrorItem.qml'
145--- qml/mcloud/ErrorItem.qml 1970-01-01 00:00:00 +0000
146+++ qml/mcloud/ErrorItem.qml 2016-05-31 09:02:02 +0000
147@@ -0,0 +1,48 @@
148+/*
149+ * Copyright (C) 2015 Canonical Ltd.
150+ *
151+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
152+ *
153+ * This program is free software: you can redistribute it and/or modify it
154+ * under the terms of the GNU General Public License version 3, as published
155+ * by the Free Software Foundation.
156+ *
157+ * This program is distributed in the hope that it will be useful, but
158+ * WITHOUT ANY WARRANTY; without even the implied warranties of
159+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
160+ * PURPOSE. See the GNU General Public License for more details.
161+ *
162+ * You should have received a copy of the GNU General Public License along
163+ * with this program. If not, see <http://www.gnu.org/licenses/>.
164+ */
165+
166+import QtQuick 2.0
167+import Ubuntu.Components 0.1
168+import Ubuntu.Components.ListItems 0.1 as ListItem
169+
170+Item {
171+ id: root
172+
173+ signal retryRequested()
174+
175+ Column {
176+ anchors {
177+ verticalCenter: parent.verticalCenter
178+ left: parent.left; right: parent.right
179+ }
180+ spacing: units.gu(2)
181+
182+ Label {
183+ anchors { left: parent.left; right: parent.right }
184+ text: i18n.dtr("ubuntu-system-settings-online-accounts", "This service is not available right now. Try again later.")
185+ wrapMode: Text.WordWrap
186+ horizontalAlignment: Text.AlignHCenter
187+ }
188+
189+ Button {
190+ anchors.horizontalCenter: parent.horizontalCenter
191+ text: i18n.dtr("ubuntu-system-settings-online-accounts", "Try Again")
192+ onClicked: root.retryRequested()
193+ }
194+ }
195+}
196
197=== added file 'qml/mcloud/Main.qml'
198--- qml/mcloud/Main.qml 1970-01-01 00:00:00 +0000
199+++ qml/mcloud/Main.qml 2016-05-31 09:02:02 +0000
200@@ -0,0 +1,7 @@
201+import Ubuntu.OnlineAccounts.Plugin 1.0
202+import "." as Local
203+
204+OAuthMain {
205+ creationComponent: Local.OAuth {
206+ }
207+}
208
209=== added file 'qml/mcloud/OAuth.qml'
210--- qml/mcloud/OAuth.qml 1970-01-01 00:00:00 +0000
211+++ qml/mcloud/OAuth.qml 2016-05-31 09:02:02 +0000
212@@ -0,0 +1,283 @@
213+/*
214+ * Copyright (C) 2013-2016 Canonical Ltd.
215+ *
216+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
217+ *
218+ * This program is free software: you can redistribute it and/or modify it
219+ * under the terms of the GNU General Public License version 3, as published
220+ * by the Free Software Foundation.
221+ *
222+ * This program is distributed in the hope that it will be useful, but
223+ * WITHOUT ANY WARRANTY; without even the implied warranties of
224+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
225+ * PURPOSE. See the GNU General Public License for more details.
226+ *
227+ * You should have received a copy of the GNU General Public License along
228+ * with this program. If not, see <http://www.gnu.org/licenses/>.
229+ */
230+
231+import QtQuick 2.0
232+import Ubuntu.Components 1.3
233+import Ubuntu.Components.ListItems 1.3 as ListItem
234+import Ubuntu.Components.Popups 1.3
235+import Ubuntu.OnlineAccounts 0.1
236+import Ubuntu.OnlineAccounts.Plugin 1.0
237+
238+Item {
239+ id: root
240+
241+ /* To override the parameters coming from the .provider file: */
242+ property variant authenticationParameters: {}
243+ /* To override the default access control list: */
244+ property variant accessControlList: ["unconfined"]
245+
246+ property variant authReply
247+ property bool isNewAccount: false
248+ property variant __account: account
249+ property bool __isAuthenticating: false
250+ property alias globalAccountService: globalAccountSettings
251+ property bool loading: loader.status == Loader.Null || loader.status == Loader.Loading
252+
253+ signal authenticated(variant reply)
254+ signal authenticationError(variant error)
255+ signal finished
256+
257+ anchors.fill: parent
258+
259+ Component.onCompleted: {
260+ isNewAccount = (account.accountId === 0)
261+ enableAccount()
262+ authenticate()
263+ }
264+
265+ RequestHandler {
266+ id: requestHandler
267+ onRequestChanged: {
268+ if (request) {
269+ console.log("RequestHandler captured request!")
270+ loader.setSource("WebView.qml", {
271+ "signonRequest": request
272+ })
273+ } else {
274+ console.log("Request destroyed!")
275+ loader.source = ""
276+ }
277+ }
278+ }
279+
280+ Credentials {
281+ id: creds
282+ caption: account.provider.id
283+ acl: accessControlList
284+ onCredentialsIdChanged: root.credentialsStored()
285+ }
286+
287+ AccountService {
288+ id: globalAccountSettings
289+ objectHandle: account.accountServiceHandle
290+ credentials: creds
291+ autoSync: false
292+
293+ onAuthenticated: {
294+ __isAuthenticating = false
295+ authReply = reply
296+ root.authenticated(reply)
297+ }
298+ onAuthenticationError: {
299+ __isAuthenticating = false
300+ root.authenticationError(error)
301+ }
302+ }
303+
304+ AccountServiceModel {
305+ id: accountServices
306+ includeDisabled: true
307+ account: __account.objectHandle
308+ }
309+
310+ ListItem.Base {
311+ visible: loading && !errorItem.visible
312+ height: units.gu(7)
313+ showDivider: false
314+ anchors.top: parent.top
315+
316+ Item {
317+ height: units.gu(5)
318+ width: units.gu(30)
319+ anchors.horizontalCenter: parent.horizontalCenter
320+ anchors.top: parent.top
321+ anchors.margins: units.gu(1)
322+
323+ ActivityIndicator {
324+ id: loadingIndicator
325+ anchors.verticalCenter: parent.verticalCenter
326+ anchors.left: parent.left
327+ anchors.leftMargin: units.gu(5)
328+ running: loading
329+ z: 1
330+ }
331+ Label {
332+ text: i18n.dtr("ubuntu-system-settings-online-accounts", "Loading…")
333+ anchors.verticalCenter: parent.verticalCenter
334+ anchors.left: loadingIndicator.right
335+ anchors.leftMargin: units.gu(3)
336+ }
337+ }
338+ }
339+
340+ Loader {
341+ id: loader
342+ anchors {
343+ top: parent.top
344+ left: parent.left
345+ right: parent.right
346+ bottom: Qt.inputMethod.visible ? osk.top : cancelButton.top
347+ }
348+ focus: true
349+ visible: !loading
350+ }
351+
352+ ErrorItem {
353+ id: errorItem
354+ anchors { fill: parent; margins: units.gu(4) }
355+ visible: false
356+ onRetryRequested: {
357+ root.credentialsStored()
358+ visible = false
359+ }
360+ }
361+
362+ KeyboardRectangle {
363+ id: osk
364+ }
365+
366+ ListItem.SingleControl {
367+ id: cancelButton
368+ anchors.bottom: parent.bottom
369+ showDivider: false
370+ control: Button {
371+ text: i18n.dtr("ubuntu-system-settings-online-accounts", "Cancel")
372+ width: parent.width - units.gu(4)
373+ onClicked: root.cancel()
374+ }
375+ }
376+
377+ AccountServiceModel {
378+ id: possiblyDuplicateAccounts
379+ service: "global"
380+ provider: __account.provider.id
381+ }
382+
383+ function authenticate() {
384+ console.log("Authenticating...")
385+ creds.sync()
386+ }
387+
388+ function credentialsStored() {
389+ console.log("Credentials stored, id: " + creds.credentialsId)
390+ if (creds.credentialsId == 0) return
391+ var parameters = {}
392+ parameters[requestHandler.matchKey] = requestHandler.matchId
393+ parameters["providerId"] = account.provider.id
394+ for (var p in authenticationParameters) {
395+ parameters[p] = authenticationParameters[p]
396+ }
397+ __isAuthenticating = true
398+ globalAccountSettings.authenticate(parameters)
399+ }
400+
401+ function cancel() {
402+ if (__isAuthenticating) {
403+ /* This will cause the authentication to fail, and this method will
404+ * be invoked again to delete the credentials. */
405+ globalAccountSettings.cancelAuthentication()
406+ return
407+ }
408+ if (isNewAccount && creds.credentialsId != 0) {
409+ console.log("Removing credentials...")
410+ creds.remove()
411+ creds.removed.connect(finished)
412+ } else {
413+ finished()
414+ }
415+ }
416+
417+ function enableAccount() {
418+ globalAccountSettings.updateServiceEnabled(true)
419+ }
420+
421+ function getUserName(reply, callback) {
422+ /* This should work for OAuth 1.0a; for OAuth 2.0 this function needs
423+ * to be reimplemented */
424+ if ('ScreenName' in reply) return reply.ScreenName
425+ else if ('UserId' in reply) return reply.UserId
426+ return ''
427+ }
428+
429+ function accountIsDuplicate(userName) {
430+ var model = possiblyDuplicateAccounts
431+ for (var i = 0; i < model.count; i++) {
432+ if (model.get(i, "displayName") == userName)
433+ return true
434+ }
435+ return false
436+ }
437+
438+ function __gotUserName(userName, reply) {
439+ console.log("UserName: " + userName)
440+ if (userName != '') {
441+ if (accountIsDuplicate(userName)) {
442+ var dialog = PopupUtils.open(Qt.resolvedUrl("DuplicateAccount.qml"))
443+ dialog.closed.connect(cancel)
444+ return
445+ }
446+ account.updateDisplayName(userName)
447+ }
448+ beforeSaving(reply)
449+ }
450+
451+ function saveAccount() {
452+ account.synced.connect(finished)
453+ account.sync()
454+ }
455+
456+ /* reimplement this function in plugins in order to perform some actions
457+ * before quitting the plugin */
458+ function beforeSaving(reply) {
459+ saveAccount()
460+ }
461+
462+ function __getUserNameAndSave(reply) {
463+ /* If the completeCreation function is defined, run it */
464+ if (typeof(completeCreation) == "function") {
465+ console.warn("The completeCreation method is deprecated; use getUserName() or beforeSaving() instead")
466+ completeCreation(reply)
467+ return
468+ }
469+
470+ var userName = getUserName(reply, function(name) {
471+ __gotUserName(name, reply)
472+ })
473+ if (typeof(userName) == "string") {
474+ __gotUserName(userName, reply)
475+ } else if (userName === false) {
476+ cancel()
477+ return
478+ }
479+ // otherwise (userName === true), wait for the callback to be invoked
480+ }
481+
482+ onAuthenticated: __getUserNameAndSave(reply)
483+
484+ onAuthenticationError: {
485+ console.log("Authentication error, code " + error.code)
486+ if (error.code == AccountService.NetworkError) {
487+ console.log("Network error")
488+ errorItem.visible = true
489+ return
490+ }
491+ root.cancel()
492+ }
493+
494+ onFinished: loading = false
495+}
496
497=== added file 'qml/mcloud/WebView.qml'
498--- qml/mcloud/WebView.qml 1970-01-01 00:00:00 +0000
499+++ qml/mcloud/WebView.qml 2016-05-31 09:02:02 +0000
500@@ -0,0 +1,94 @@
501+import QtQuick 2.0
502+import Ubuntu.Components 1.1
503+import Ubuntu.Web 0.2
504+
505+WebView {
506+ id: root
507+
508+ property QtObject signonRequest
509+ readonly property string token_url: "https://ose.caiyun.feixin.10086.cn/oauthApp/OAuth2/getToken"
510+ readonly property string redirect_url: "http://developer.ubuntu.com/en/"
511+ readonly property string client_id: "APP1ZtqoN3R0002"
512+ readonly property string client_pass: "A70EFCDC91456349E7FDECF0A33574AC"
513+
514+ Component.onCompleted: {
515+ signonRequest.authenticated.connect(onAuthenticated)
516+ url = signonRequest.startUrl
517+ }
518+
519+ //1.WORKAROUND: we need to retrieve refresh token instead of access token.
520+ function fetchRefreshToken(code) {
521+ var http = new XMLHttpRequest()
522+ var body = "grant_type=authorization_code&code="+code+"&redirect_uri="+redirect_url
523+
524+ http.open("POST", token_url, true);
525+ http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
526+ http.setRequestHeader("Authorization", "Basic " + Qt.btoa(client_id + ":" + client_pass));
527+ http.onreadystatechange = function() {
528+ if (http.readyState === 4 && http.status == 200) {
529+ var response = JSON.parse(http.responseText)
530+ //create a fake access_token with refresh token.
531+ var urlStr = redirect_url+"#access_token="+response.refresh_token
532+ var authUrl = Qt.resolvedUrl(urlStr);
533+ signonRequest.currentUrl = authUrl
534+ } else {
535+ console.log("error: " + http.status)
536+ }
537+ };
538+
539+ http.send(body);
540+ }
541+
542+ onLoadingStateChanged: {
543+ if (loading) {
544+ signonRequest.onLoadStarted()
545+ } else if (lastLoadSucceeded) {
546+ signonRequest.onLoadFinished(true)
547+ } else {
548+ signonRequest.onLoadFinished(false)
549+ }
550+ }
551+
552+ onUrlChanged: {
553+ //2.WORKAROUND: retrieve refresh token if code is fetched
554+ var code_pattern= new RegExp("\\?code=.*&");
555+ var urlStr = url.toString()
556+ if (code_pattern.test(urlStr)){
557+ var code = urlStr.match(/\?code=(.*)&/)[1];
558+ console.log("code::", code)
559+ fetchRefreshToken(code)
560+ } else {
561+ signonRequest.currentUrl = url
562+ }
563+ }
564+
565+ //3.WORKAROUND: Apply desktop useragent to prevent server from detecting android client,
566+ //which causes authentication failure.
567+ context: WebContext {
568+ dataPath: signonRequest ? signonRequest.rootDir : ""
569+ userAgent: "Mozilla/5.0 (Linux; Ubuntu 14.04) AppleWebKit/537.36 Chromium/35.0.1870.2 Safari/537.36"
570+ }
571+
572+ function onAuthenticated() {
573+ /* Get the cookies and set them on the request */
574+ console.log("Authenticated; getting cookies")
575+ context.cookieManager.getCookiesResponse.connect(onGotCookies)
576+ context.cookieManager.getAllCookies()
577+ visible = false
578+ }
579+
580+ function onGotCookies(requestId, cookies) {
581+ signonRequest.setCookies(cookies)
582+ }
583+
584+ /* Taken from webbrowser-app */
585+ ProgressBar {
586+ anchors.top: parent.top
587+ anchors.left: parent.left
588+ anchors.right: parent.right
589+ height: units.dp(3)
590+ showProgressPercentage: false
591+ visible: root.loading
592+ value: root.loadProgress / 100
593+ }
594+}

Subscribers

People subscribed via source and target branches