auth.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. """Wrapper for Google OAuth2 API."""
  2. import sys
  3. import json
  4. import googleapiclient.discovery
  5. import oauth2client
  6. import httplib2
  7. import lib
  8. try:
  9. from PyQt4 import QtCore, QtGui, QtWebKit
  10. WEBKIT_BACKEND = "qt"
  11. except ImportError:
  12. import gtk
  13. import webkit
  14. WEBKIT_BACKEND = "gtk"
  15. except ImportError:
  16. WEBKIT_BACKEND = None
  17. YOUTUBE_UPLOAD_SCOPE = "https://www.googleapis.com/auth/youtube.upload"
  18. CHECK_AUTH_JS = """
  19. var code = document.getElementById("code");
  20. var access_denied = document.getElementById("access_denied");
  21. var result;
  22. if (code) {
  23. result = {authorized: true, code: code.value};
  24. } else if (access_denied) {
  25. result = {authorized: false, message: access_denied.innerText};
  26. } else {
  27. result = {};
  28. }
  29. """
  30. CHECK_AUTH_JS_GTK = CHECK_AUTH_JS + "window.status = JSON.stringify(result);"
  31. CHECK_AUTH_JS_QT = CHECK_AUTH_JS + "result;"
  32. def _get_code_from_browser(url, size=(640, 480), title="Google authentication"):
  33. if WEBKIT_BACKEND == "qt":
  34. lib.debug("Using webkit backend: QT")
  35. with lib.default_sigint():
  36. return _get_code_from_browser_qt(url, size=size, title=title)
  37. elif WEBKIT_BACKEND == "gtk":
  38. lib.debug("Using webkit backend: GTK")
  39. return _get_code_from_browser_gtk(url, size=size, title=title)
  40. else:
  41. raise NotImplementedError("Install pywebkitgtk or qtwebkit")
  42. def on_qt_page_load_finished(dialog, webview):
  43. to_s = lambda x: (str(x.toUtf8()) if isinstance(x, QtCore.QString) else x)
  44. frame = webview.page().currentFrame()
  45. jscode = QtCore.QString(CHECK_AUTH_JS_QT)
  46. res = frame.evaluateJavaScript(jscode)
  47. authorization = dict((to_s(k), to_s(v)) for (k, v) in res.toPyObject().items())
  48. if authorization:
  49. dialog.authorization_code = authorization.get("code")
  50. dialog.close()
  51. def _get_code_from_browser_qt(url, size=(640, 480), title="Google authentication"):
  52. app = QtGui.QApplication(sys.argv)
  53. dialog = QtGui.QDialog()
  54. dialog.setWindowTitle(title)
  55. dialog.resize(*size)
  56. webview = QtWebKit.QWebView()
  57. webpage = QtWebKit.QWebPage()
  58. webview.setPage(webpage)
  59. webpage.loadFinished.connect(lambda: on_qt_page_load_finished(dialog, webview))
  60. webview.setUrl(QtCore.QUrl.fromEncoded(url))
  61. layout = QtGui.QGridLayout()
  62. layout.addWidget(webview)
  63. dialog.setLayout(layout)
  64. dialog.authorization_code = None
  65. dialog.show()
  66. app.exec_()
  67. return dialog.authorization_code
  68. def _on_webview_status_bar_changed(webview, status, dialog):
  69. if status:
  70. result = json.loads(status)
  71. if result.has_key("authorized"):
  72. dialog.set_data("authorization", result)
  73. dialog.response(0)
  74. def _get_code_from_browser_gtk(url, size=(640, 480), title="Google authentication"):
  75. """Open a webkit window and return the code the user wrote."""
  76. dialog = gtk.Dialog(title=title)
  77. webview = webkit.WebView()
  78. scrolled = gtk.ScrolledWindow()
  79. scrolled.add(webview)
  80. dialog.get_children()[0].add(scrolled)
  81. webview.load_uri(url)
  82. dialog.resize(*size)
  83. dialog.show_all()
  84. dialog.connect("delete-event", lambda event, data: dialog.response(1))
  85. webview.connect("load-finished",
  86. lambda view, frame: view.execute_script(CHECK_AUTH_JS))
  87. webview.connect("status-bar-text-changed", _on_webview_status_bar_changed, dialog)
  88. dialog.set_data("authorization", None)
  89. status = dialog.run()
  90. dialog.destroy()
  91. while gtk.events_pending():
  92. gtk.main_iteration(False)
  93. authorization = dialog.get_data("authorization")
  94. if status == 0 and authorization and authorization.get("authorized"):
  95. return authorization["code"]
  96. def _get_code_from_prompt(authorize_url):
  97. """Show authorization URL and return the code the user wrote."""
  98. message = "Check this link in your browser: {0}".format(authorize_url)
  99. lib.debug(message)
  100. return raw_input("Enter verification code: ")
  101. def _get_credentials_interactively(flow, storage, get_code_callback):
  102. """Return the credentials asking the user."""
  103. flow.redirect_uri = oauth2client.client.OOB_CALLBACK_URN
  104. authorize_url = flow.step1_get_authorize_url()
  105. code = get_code_callback(authorize_url)
  106. if code:
  107. credential = flow.step2_exchange(code, http=None)
  108. storage.put(credential)
  109. credential.set_store(storage)
  110. return credential
  111. def _get_credentials(flow, storage, get_code_callback):
  112. """Return the user credentials. If not found, run the interactive flow."""
  113. existing_credentials = storage.get()
  114. if existing_credentials and not existing_credentials.invalid:
  115. return existing_credentials
  116. else:
  117. return _get_credentials_interactively(flow, storage, get_code_callback)
  118. def get_resource(client_secrets_file, credentials_file, get_code_callback=None):
  119. """Authenticate and return a googleapiclient.discovery.Resource object."""
  120. get_flow = oauth2client.client.flow_from_clientsecrets
  121. flow = get_flow(client_secrets_file, scope=YOUTUBE_UPLOAD_SCOPE)
  122. storage = oauth2client.file.Storage(credentials_file)
  123. get_code = get_code_callback or _get_code_from_prompt
  124. credentials = _get_credentials(flow, storage, get_code)
  125. if credentials:
  126. http = credentials.authorize(httplib2.Http())
  127. return googleapiclient.discovery.build("youtube", "v3", http=http)