auth.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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 _on_qt_page_load_finished(dialog, webview):
  33. to_s = lambda x: (str(x.toUtf8()) if isinstance(x, QtCore.QString) else x)
  34. frame = webview.page().currentFrame()
  35. jscode = QtCore.QString(CHECK_AUTH_JS_QT)
  36. res = frame.evaluateJavaScript(jscode)
  37. authorization = dict((to_s(k), to_s(v)) for (k, v) in res.toPyObject().items())
  38. if authorization.has_key("authorized"):
  39. dialog.authorization_code = authorization.get("code")
  40. dialog.close()
  41. def _get_code_from_browser_qt(url, size, title):
  42. app = QtGui.QApplication([])
  43. dialog = QtGui.QDialog()
  44. dialog.setWindowTitle(title)
  45. dialog.resize(*size)
  46. webview = QtWebKit.QWebView()
  47. webpage = QtWebKit.QWebPage()
  48. webview.setPage(webpage)
  49. webpage.loadFinished.connect(lambda: _on_qt_page_load_finished(dialog, webview))
  50. webview.setUrl(QtCore.QUrl.fromEncoded(url))
  51. layout = QtGui.QGridLayout()
  52. layout.addWidget(webview)
  53. dialog.setLayout(layout)
  54. dialog.authorization_code = None
  55. dialog.show()
  56. app.exec_()
  57. return dialog.authorization_code
  58. def _on_webview_status_bar_changed(webview, status, dialog):
  59. if status:
  60. authorization = json.loads(status)
  61. if authorization.has_key("authorized"):
  62. dialog.set_data("authorization_code", authorization["code"])
  63. dialog.response(0)
  64. def _get_code_from_browser_gtk(url, size, title):
  65. """Open a webkit window and return the code the user wrote."""
  66. dialog = gtk.Dialog(title=title)
  67. webview = webkit.WebView()
  68. scrolled = gtk.ScrolledWindow()
  69. scrolled.add(webview)
  70. dialog.get_children()[0].add(scrolled)
  71. webview.load_uri(url)
  72. dialog.resize(*size)
  73. dialog.show_all()
  74. dialog.connect("delete-event", lambda event, data: dialog.response(1))
  75. webview.connect("load-finished",
  76. lambda view, frame: view.execute_script(CHECK_AUTH_JS_GTK))
  77. webview.connect("status-bar-text-changed", _on_webview_status_bar_changed, dialog)
  78. dialog.set_data("authorization_code", None)
  79. status = dialog.run()
  80. dialog.destroy()
  81. while gtk.events_pending():
  82. gtk.main_iteration(False)
  83. return dialog.get_data("authorization_code")
  84. def _get_credentials_interactively(flow, storage, get_code_callback):
  85. """Return the credentials asking the user."""
  86. flow.redirect_uri = oauth2client.client.OOB_CALLBACK_URN
  87. authorize_url = flow.step1_get_authorize_url()
  88. code = get_code_callback(authorize_url)
  89. if code:
  90. credential = flow.step2_exchange(code, http=None)
  91. storage.put(credential)
  92. credential.set_store(storage)
  93. return credential
  94. def _get_credentials(flow, storage, get_code_callback):
  95. """Return the user credentials. If not found, run the interactive flow."""
  96. existing_credentials = storage.get()
  97. if existing_credentials and not existing_credentials.invalid:
  98. return existing_credentials
  99. else:
  100. return _get_credentials_interactively(flow, storage, get_code_callback)
  101. def get_code_from_prompt(authorize_url):
  102. """Show authorization URL and return the code the user wrote."""
  103. message = "Check this link in your browser: {0}".format(authorize_url)
  104. lib.debug(message)
  105. return raw_input("Enter verification code: ")
  106. def get_code_from_browser(url, size=(640, 480), title="Google authentication"):
  107. if WEBKIT_BACKEND == "qt":
  108. lib.debug("Using webkit backend: QT")
  109. with lib.default_sigint():
  110. return _get_code_from_browser_qt(url, size=size, title=title)
  111. elif WEBKIT_BACKEND == "gtk":
  112. lib.debug("Using webkit backend: GTK")
  113. with lib.default_sigint():
  114. return _get_code_from_browser_gtk(url, size=size, title=title)
  115. else:
  116. raise NotImplementedError("GUI auth requires pywebkitgtk or qtwebkit")
  117. def get_resource(client_secrets_file, credentials_file, get_code_callback=None):
  118. """Authenticate and return a googleapiclient.discovery.Resource object."""
  119. get_flow = oauth2client.client.flow_from_clientsecrets
  120. flow = get_flow(client_secrets_file, scope=YOUTUBE_UPLOAD_SCOPE)
  121. storage = oauth2client.file.Storage(credentials_file)
  122. get_code = get_code_callback or get_code_from_prompt
  123. credentials = _get_credentials(flow, storage, get_code)
  124. if credentials:
  125. http = credentials.authorize(httplib2.Http())
  126. return googleapiclient.discovery.build("youtube", "v3", http=http)