Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

312 lines (273 sloc) 11.334 kb
# CUPS Cloudprint - Print via Google Cloud Print
# Copyright (C) 2011 Simon Cadman
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import os
import sys
import subprocess
# workaround for ubuntu 12.04 / older python-six version
try:
from six.moves import urllib
except ImportError:
import six
import urllib
import urlparse
six.moves.urllib = urllib
six.moves.urllib.parse = urlparse
six.moves.urllib.parse.urlencode = urllib.urlencode
from oauth2client import client
from oauth2client import multistore_file
from cloudprintrequestor import CloudPrintRequestor
from ccputils import Utils
from oauth2client.client import AccessTokenRefreshError
class Auth(object):
clientid = "843805314553.apps.googleusercontent.com"
clientsecret = 'MzTBsY4xlrD_lxkmwFbBrvBv'
config = '/etc/cloudprint.conf'
normal_permissions = 'https://www.googleapis.com/auth/cloudprint'
http_thread = None
httpd = None
code = None
@staticmethod
def RenewToken(interactive, requestor, credentials, storage, userid):
try:
credentials.refresh(requestor)
except AccessTokenRefreshError as e:
if not interactive:
message = "ERROR: Failed to renew token for " + str(userid)
message += " (error: "
message += str(e)
message += "), "
message += "please re-run "
message += "/usr/share/cloudprint-cups/"
message += "setupcloudprint.py\n"
sys.stderr.write(message)
sys.exit(1)
else:
message = "Failed to renew token for " + str(userid) + " (error: "
message += str(e) + "), "
message += "authentication needs to be "
message += "setup again:\n"
sys.stderr.write(message)
Auth.AddAccount(storage, userid)
credentials = storage.get()
return credentials
@staticmethod
def DeleteAccount(userid):
"""Delete an account from the configuration file
Args:
userid: string, reference for the account
Returns:
deleted: boolean , true on success
"""
storage = multistore_file.get_credential_storage(
Auth.config,
Auth.clientid,
userid,
Auth.normal_permissions)
return storage.delete()
@staticmethod
def SetupHttpReturnServer():
import BaseHTTPServer
import random
import SocketServer
from threading import Thread
handler = BaseHTTPServer.BaseHTTPRequestHandler
def do_GET(self):
self.send_response(200)
if "code=" in self.path:
Auth.code = self.path[self.path.index('code=') + 5:]
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write("<html><head></head><body>Thank you !" +
" Your Google printers will be added, you can " +
"now close this window</body></html>")
handler.do_GET = do_GET
while (Auth.httpd is None):
try:
port = random.randint(12000, 20000)
Auth.httpd = SocketServer.TCPServer(("", port), handler)
except Exception:
pass
def http_server():
Auth.httpd.serve_forever(0.5)
Auth.http_thread = Thread(target=http_server)
Auth.http_thread.start()
return "http://localhost:%d/" % port
@staticmethod
def AddAccount(storage, userid=None, permissions=None):
"""Adds an account to the configuration file with an interactive dialog.
Args:
storage: storage, instance of storage to store credentials in.
userid: string, reference for the account
permissions: string or iterable of strings, scope(s) of the credentials being requested
Returns:
credentials: A credentials instance with the account details
"""
if permissions is None:
permissions = Auth.normal_permissions
if userid is None:
userid = raw_input(
"Name for this user account ( eg something@gmail.com )? ")
# setup storage again if just got userid now
storage = multistore_file.get_credential_storage(
Auth.config,
Auth.clientid,
userid,
permissions)
url = None
while True:
if Utils.hasGUI():
url = Auth.SetupHttpReturnServer()
Auth.code = None
flow, auth_uri = Auth.AddAccountStep1(userid, permissions, url)
message = "Open this URL if it doesn't, grant access to CUPS Cloud Print "
message += "( for the " + userid + " account ), "
message += "then provide the code displayed : \n\n"
message += auth_uri + "\n"
print message
Utils.openBrowserWithUrl(auth_uri)
if url is not None:
from select import select
print 'Code from Google: '
while (Auth.code is None):
result, _, _ = select([sys.stdin], [], [], 0.5)
if result and Auth.code is None:
s = sys.stdin.readline()
if s != "":
Auth.code = s
Auth.httpd.shutdown()
else:
Auth.code = raw_input('Code from Google: ')
try:
credentials = Auth.AddAccountStep2(userid, flow, Auth.code, storage, permissions)
return credentials
except Exception as e:
message = "\nThe code does not seem to be valid ( "
message += str(e) + " ), please try again.\n"
print message
@staticmethod
def AddAccountStep1(userid, permissions=None, redirect_uri=None):
"""Executes step 1 of OAuth2WebServerFlow, without interaction.
Args:
userid: string, reference for the account
permissions: string or iterable of strings, scope(s) of the credentials being requested
Returns:
tuple of:
OAuth2WebServerFlow instance
string auth_uri for user to visit
"""
if redirect_uri is None:
redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
if permissions is None:
permissions = Auth.normal_permissions
flow = client.OAuth2WebServerFlow(
client_id=Auth.clientid,
client_secret=Auth.clientsecret,
scope=permissions,
user_agent=userid,
redirect_uri=redirect_uri)
auth_uri = flow.step1_get_authorize_url()
return flow, auth_uri
@staticmethod
def AddAccountStep2(userid, flow, code, storage=None, permissions=None):
"""Executes step 2 of OAuth2WebServerFlow, without interaction.
Args:
userid: string, reference for the account
permissions: string or iterable of strings, scope(s) of the credentials being requested
storage: storage, instance of storage to store credentials in.
flow: OAuth2WebServerFlow, flow instance
code: string, code representing user granting CCP permission to call GCP API for user
Returns:
credentials: A credentials instance with the account details
"""
if permissions is None:
permissions = Auth.normal_permissions
if storage is None:
storage = multistore_file.get_credential_storage(
Auth.config,
Auth.clientid,
userid,
permissions)
credentials = flow.step2_exchange(code)
storage.put(credentials)
Utils.FixFilePermissions(Auth.config)
return credentials
@staticmethod
def SetupAuth(interactive=False, permissions=None, testUserIds=None):
"""Sets up requestors with authentication tokens
Args:
interactive: boolean, when set to true can prompt user, otherwise
returns False if authentication fails
Returns:
requestor, storage: Authenticated requestors and an instance
of storage
"""
if permissions is None:
permissions = Auth.normal_permissions
modifiedconfig = False
# parse config file and extract useragents, which we use for account
# names
userids = []
if testUserIds is not None:
userids = testUserIds
if os.path.exists(Auth.config):
data = json.loads(Utils.ReadFile(Auth.config))
if 'data' in data:
for user in data['data']:
userids.append(str(user['credential']['user_agent']))
else:
Utils.WriteFile(Auth.config, '{}')
Utils.FixFilePermissions(Auth.config)
modifiedconfig = True
if len(userids) == 0:
userids = [None]
requestors = []
storage = None
credentials = None
for userid in userids:
if userid is not None:
storage = multistore_file.get_credential_storage(
Auth.config,
Auth.clientid,
userid,
permissions)
credentials = storage.get()
if not credentials and interactive:
credentials = Auth.AddAccount(storage, userid, permissions)
modifiedconfig = True
if userid is None:
userid = credentials.user_agent
if credentials:
# renew if expired
requestor = CloudPrintRequestor()
if credentials.access_token_expired:
Auth.RenewToken(interactive, requestor, credentials, storage, userid)
requestor = credentials.authorize(requestor)
requestor.setAccount(userid)
requestors.append(requestor)
# fix permissions
if modifiedconfig:
Utils.FixFilePermissions(Auth.config)
if not credentials:
return False, False
else:
return requestors, storage
@staticmethod
def GetAccountNames(requestors):
requestorAccounts = []
for requestor in requestors:
requestorAccounts.append(requestor.getAccount())
return requestorAccounts
Jump to Line
Something went wrong with that request. Please try again.