/usr/share/goa-1.0/scripts/lpa_helper.py is in gnome-online-accounts 3.28.0-0ubuntu2.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | #!/usr/bin/env python3
import json
import os
import sys
from urllib.parse import urlencode
import requests # fades
import macaroonbakery # fades
import pymacaroons # fades
from macaroonbakery import httpbakery # fades
LIVEPATCH_AUTH_ROOT_URL = os.environ.get(
'LIVEPATCH_AUTH_ROOT_URL', 'https://auth.livepatch.canonical.com')
UBUNTU_SSO_ROOT_URL = os.environ.get(
'UBUNTU_SSO_ROOT_URL', 'https://login.ubuntu.com')
generic_error = "GENERIC_ERROR"
class AuthenticationFailed(Exception):
def __init__(self, code, msg):
super(AuthenticationFailed, self).__init__(msg)
self.code = code
class USSOMacaroonInteractor(httpbakery.LegacyInteractor):
def __init__(self, session):
self.session = requests.Session()
def kind(self):
return "interactive"
def legacy_interact(self, client, location, visit_url):
# get usso_macaroon
usso_url = self.get_usso_macaroon_url(visit_url)
usso_macaroon = self.get_usso_macaroon(usso_url)
def callback(discharges):
self.complete_usso_macaroon_discharge(usso_url, discharges)
discharges = self.discharge_usso_macaroon(usso_macaroon, callback)
def get_usso_macaroon_url(self, url):
# find interaction methods for discharge
response = self.session.get(
url, headers={'Accept': 'application/json'})
if not response.ok:
raise AuthenticationFailed(generic_error, 'can not find interaction methods')
data = response.json()
# expect usso_macaroon interaction method
if 'usso_macaroon' not in data:
raise AuthenticationFailed(generic_error, 'missing usso_macaroon interaction method')
return data['usso_macaroon']
def get_usso_macaroon(self, url):
response = self.session.get(url)
if not response.ok:
raise AuthenticationFailed(generic_error, 'can not get usso macaroon')
usso_macaroon = response.json()['macaroon']
return usso_macaroon
def discharge_usso_macaroon(self, macaroon, callback):
usso_caveats = [
cav['cid'] for cav in macaroon.get('caveats', [])
if cav.get('cl') == UBUNTU_SSO_ROOT_URL]
if len(usso_caveats) <= 0:
raise AuthenticationFailed(generic_error, 'no valid usso caveat found')
data = {'caveat_id': usso_caveats[0]}
data.update(get_usso_credentials())
def _discharge_macaroon(data):
response = self.session.post(
'{}/api/v2/tokens/discharge'.format(UBUNTU_SSO_ROOT_URL),
data=json.dumps(data),
headers={'Content-Type': 'application/json'})
return response
response = _discharge_macaroon(data)
if not response.ok:
if response.status_code in (400, 401, 403):
raise AuthenticationFailed(response.json().get('code'), response.json().get('message'))
else:
raise AuthenticationFailed(generic_error, 'authentication issue')
root_m = macaroonbakery.bakery.Macaroon.deserialize_json(
json.dumps(macaroon)).macaroon
discharge_m = pymacaroons.Macaroon.deserialize(
response.json()['discharge_macaroon'])
bound_discharge = root_m.prepare_for_request(discharge_m)
callback([root_m.serialize(), bound_discharge.serialize()])
def complete_usso_macaroon_discharge(self, url, discharges):
data = {'macaroons': discharges}
response = self.session.post(
url, data=json.dumps(data),
headers={'Content-Type': 'application/json'})
if not response.ok:
raise AuthenticationFailed(generic_error, 'can not complete usso macaroon discharge')
def get_usso_credentials():
email = input('Email: ')
password = input('Password: ')
ret = {'email': email, 'password': password}
otp = input('Two-factor code: ')
if otp and len(otp) > 0:
ret.update({'otp': otp})
return ret
def get_lpa_token(session):
url = '{}/api/v1/tokens?{}'.format(
LIVEPATCH_AUTH_ROOT_URL, urlencode({'token_type': 'user'}))
return session.get(url, timeout=10)
if __name__ == '__main__':
cookies = requests.cookies.RequestsCookieJar()
session = requests.Session()
client = httpbakery.Client(
interaction_methods=[USSOMacaroonInteractor(session)], cookies=cookies)
session.auth = client.auth()
session.cookies = cookies
try:
response = get_lpa_token(session)
if response.ok:
print(response.json()['token'], file=sys.stderr)
except AuthenticationFailed as af:
print(af.code, af, file=sys.stderr)
sys.exit(1)
except Exception as e:
print(generic_error, e, file=sys.stderr)
sys.exit(1)
|