def __call__(self, request): if not request.external_auth_info: return HttpResponseNotFound('Not found') macaroon_bakery = _get_bakery(request) req_headers = request_headers(request) auth_checker = macaroon_bakery.checker.auth( httpbakery.extract_macaroons(req_headers)) try: auth_info = auth_checker.allow( checkers.AuthContext(), [bakery.LOGIN_OP]) except bakery.DischargeRequiredError as err: return _authorization_request( macaroon_bakery, derr=err, req_headers=req_headers) except bakery.VerificationError: return _authorization_request( macaroon_bakery, req_headers=req_headers, auth_endpoint=request.external_auth_info.url, auth_domain=request.external_auth_info.domain) except bakery.PermissionDenied: return HttpResponseForbidden() user = authenticate(request, identity=auth_info.identity) if user is None: # macaroon authentication can return None if the user exists but # doesn't have permission to log in return HttpResponseForbidden('User login not allowed') login( request, user, backend='maasserver.macaroon_auth.MacaroonAuthorizationBackend') return JsonResponse( {attr: getattr(user, attr) for attr in ('id', 'username', 'is_superuser')})
def is_authenticated(self, request): if not request.external_auth_info: return False req_headers = request_headers(request) macaroon_bakery = _get_bakery(request) auth_checker = macaroon_bakery.checker.auth( httpbakery.extract_macaroons(req_headers)) try: auth_info = auth_checker.allow(checkers.AuthContext(), [bakery.LOGIN_OP]) except (bakery.DischargeRequiredError, bakery.PermissionDenied): return False # set the user in the request so that it's considered authenticated. If # a user is not found with the username from the identity, it's # created. username = auth_info.identity.id() try: user = User.objects.get(username=username) if user.userprofile.is_local: return False except User.DoesNotExist: user = User(username=username) user.save() if not validate_user_external_auth(user, request.external_auth_info): return False request.user = user return True
def __call__(self, request): auth_endpoint = Config.objects.get_config('external_auth_url') if not auth_endpoint: return HttpResponseNotFound('Not found') macaroon_bakery = self._setup_bakery(auth_endpoint, request) req_headers = request_headers(request) auth_checker = macaroon_bakery.checker.auth( httpbakery.extract_macaroons(req_headers)) try: auth_info = auth_checker.allow( checkers.AuthContext(), [bakery.LOGIN_OP]) except bakery.DischargeRequiredError as err: return self._authorization_request( request, req_headers, macaroon_bakery, err) except bakery.PermissionDenied: return HttpResponseForbidden() # a user is always returned since the authentication middleware creates # one if not found user = authenticate(request, identity=auth_info.identity) backend = ( 'maasserver.views.macaroon_auth.MacaroonAuthenticationBackend') login(request, user, backend=backend) return JsonResponse({'id': user.id, 'username': user.username})
def server_get(url, request): ctx = checkers.AuthContext() test_ops = [bakery.Op(entity='test-op', action='read')] auth_checker = server_bakery.checker.auth( httpbakery.extract_macaroons(request.headers)) try: auth_checker.allow(ctx, test_ops) resp = response(status_code=200, content='done') except bakery.PermissionDenied: caveats = [ checkers.Caveat(location='http://0.3.2.1', condition='is-ok') ] m = server_bakery.oven.macaroon( version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=caveats, ops=test_ops) content, headers = httpbakery.discharge_required_response( m, '/', 'test', 'message') resp = response(status_code=401, content=content, headers=headers) return request.hooks['response'][0](resp)
def visit(url, request): if request.headers.get('Accept') == 'application/json': return {'status_code': 200, 'content': {'agent': request.url}} cs = SimpleCookie() cookies = request.headers.get('Cookie') if cookies is not None: cs.load(str(cookies)) public_key = None for c in cs: if c == 'agent-login': json_cookie = json.loads( base64.b64decode(cs[c].value).decode('utf-8')) public_key = bakery.PublicKey.deserialize( json_cookie.get('public_key')) ms = httpbakery.extract_macaroons(request.headers) if len(ms) == 0: b = bakery.Bakery(key=discharge_key) m = b.oven.macaroon( version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=[ bakery.local_third_party_caveat( public_key, version=httpbakery.request_version( request.headers)) ], ops=[bakery.Op(entity='agent', action='login')]) content, headers = httpbakery.discharge_required_response( m, '/', 'test', 'message') resp = response(status_code=401, content=content, headers=headers) return request.hooks['response'][0](resp) return {'status_code': 200, 'content': {'agent-login': True}}
def agent_visit(url, request): if request.method != "POST": raise Exception('unexpected method') log.info('agent_visit url {}'.format(url)) body = json.loads(request.body.decode('utf-8')) if body['username'] != 'test-user': raise Exception('unexpected username in body {!r}'.format(request.body)) public_key = bakery.PublicKey.deserialize(body['public_key']) ms = httpbakery.extract_macaroons(request.headers) if len(ms) == 0: b = bakery.Bakery(key=discharge_key) m = b.oven.macaroon( version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=[bakery.local_third_party_caveat( public_key, version=httpbakery.request_version(request.headers))], ops=[bakery.Op(entity='agent', action='login')]) content, headers = httpbakery.discharge_required_response( m, '/', 'test', 'message') resp = response(status_code=401, content=content, headers=headers) return request.hooks['response'][0](resp) return { 'status_code': 200, 'content': { 'agent_login': True } }
def test_extract_macaroons_from_request(self): def encode_macaroon(m): macaroons = '[' + utils.macaroon_to_json_string(m) + ']' return base64.urlsafe_b64encode( utils.to_bytes(macaroons)).decode('ascii') req = Request('http://example.com') m1 = pymacaroons.Macaroon(version=pymacaroons.MACAROON_V2, identifier='one') req.add_header('Macaroons', encode_macaroon(m1)) m2 = pymacaroons.Macaroon(version=pymacaroons.MACAROON_V2, identifier='two') jar = requests.cookies.RequestsCookieJar() jar.set_cookie( utils.cookie( name='macaroon-auth', value=encode_macaroon(m2), url='http://example.com', )) jar.add_cookie_header(req) macaroons = httpbakery.extract_macaroons(req) self.assertEquals(len(macaroons), 2) macaroons.sort(key=lambda ms: ms[0].identifier) self.assertEquals(macaroons[0][0].identifier, m1.identifier) self.assertEquals(macaroons[1][0].identifier, m2.identifier)
def test_extract_macaroons_from_request(self): def encode_macaroon(m): macaroons = '[' + utils.macaroon_to_json_string(m) + ']' return base64.urlsafe_b64encode(utils.to_bytes(macaroons)).decode('ascii') req = Request('http://example.com') m1 = pymacaroons.Macaroon(version=pymacaroons.MACAROON_V2, identifier='one') req.add_header('Macaroons', encode_macaroon(m1)) m2 = pymacaroons.Macaroon(version=pymacaroons.MACAROON_V2, identifier='two') jar = requests.cookies.RequestsCookieJar() jar.set_cookie(utils.cookie( name='macaroon-auth', value=encode_macaroon(m2), url='http://example.com', )) jar.set_cookie(utils.cookie( name='macaroon-empty', value='', url='http://example.com', )) jar.add_cookie_header(req) macaroons = httpbakery.extract_macaroons(req) self.assertEquals(len(macaroons), 2) macaroons.sort(key=lambda ms: ms[0].identifier) self.assertEquals(macaroons[0][0].identifier, m1.identifier) self.assertEquals(macaroons[1][0].identifier, m2.identifier)
def server_get(url, request): ctx = checkers.AuthContext() test_ops = [bakery.Op(entity='test-op', action='read')] auth_checker = server_bakery.checker.auth( httpbakery.extract_macaroons(request.headers)) try: auth_checker.allow(ctx, test_ops) resp = response(status_code=200, content='done') except bakery.PermissionDenied: caveats = [ checkers.Caveat(location='http://0.3.2.1', condition='is-ok') ] m = server_bakery.oven.macaroon(version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=caveats, ops=test_ops) content, headers = httpbakery.discharge_required_response( m, '/', 'test', 'message') resp = response( status_code=401, content=content, headers=headers, ) return request.hooks['response'][0](resp)
def __call__(self, request): if not request.external_auth_info: return HttpResponseNotFound('Not found') macaroon_bakery = _get_bakery(request) req_headers = request_headers(request) auth_checker = macaroon_bakery.checker.auth( httpbakery.extract_macaroons(req_headers)) try: auth_info = auth_checker.allow( checkers.AuthContext(), [bakery.LOGIN_OP]) except bakery.DischargeRequiredError as err: return _authorization_request( macaroon_bakery, derr=err, req_headers=req_headers) except bakery.VerificationError: return _authorization_request( macaroon_bakery, req_headers=req_headers, auth_endpoint=request.external_auth_info.url) except bakery.PermissionDenied: return HttpResponseForbidden() # a user is always returned since the authentication middleware creates # one if not found user = authenticate(request, identity=auth_info.identity) login( request, user, backend='maasserver.macaroon_auth.MacaroonAuthorizationBackend') return JsonResponse({'id': user.id, 'username': user.username})
def is_authenticated(self, request): if not request.external_auth_info: return False req_headers = request_headers(request) macaroon_bakery = _get_bakery(request) auth_checker = macaroon_bakery.checker.auth( httpbakery.extract_macaroons(req_headers)) try: auth_info = auth_checker.allow( checkers.AuthContext(), [bakery.LOGIN_OP]) except (bakery.DischargeRequiredError, bakery.PermissionDenied): return False # set the user in the request so that it's considered authenticated. If # a user is not found with the username from the identity, it's # created. user, created = User.objects.get_or_create( username=auth_info.identity.id(), defaults={'is_superuser': True}) # Only check the user with IDM again if it wasn't just created if not created and not validate_user_external_auth(user): return False request.user = user return True
def agent_visit(url, request): if request.method != "POST": raise Exception('unexpected method') log.info('agent_visit url {}'.format(url)) body = json.loads(request.body.decode('utf-8')) if body['username'] != 'test-user': raise Exception('unexpected username in body {!r}'.format( request.body)) public_key = bakery.PublicKey.deserialize(body['public_key']) ms = httpbakery.extract_macaroons(request.headers) if len(ms) == 0: b = bakery.Bakery(key=discharge_key) m = b.oven.macaroon( version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=[ bakery.local_third_party_caveat( public_key, version=httpbakery.request_version( request.headers)) ], ops=[bakery.Op(entity='agent', action='login')]) content, headers = httpbakery.discharge_required_response( m, '/', 'test', 'message') resp = response(status_code=401, content=content, headers=headers) return request.hooks['response'][0](resp) return {'status_code': 200, 'content': {'agent_login': True}}
def is_authorized(*args, **kwargs): macaroon_bakery = bakery.Bakery( location="ubuntu.com/security", locator=httpbakery.ThirdPartyLocator(), identity_client=IdentityClient(), key=bakery.generate_key(), root_key_store=bakery.MemoryKeyStore( flask.current_app.config["SECRET_KEY"]), ) macaroons = httpbakery.extract_macaroons(flask.request.headers) auth_checker = macaroon_bakery.checker.auth(macaroons) launchpad = Launchpad.login_anonymously("ubuntu.com/security", "production", version="devel") try: auth_info = auth_checker.allow(checkers.AuthContext(), [bakery.LOGIN_OP]) except bakery._error.DischargeRequiredError: macaroon = macaroon_bakery.oven.macaroon( version=bakery.VERSION_2, expiry=datetime.utcnow() + timedelta(days=1), caveats=IDENTITY_CAVEATS, ops=[bakery.LOGIN_OP], ) content, headers = httpbakery.discharge_required_response( macaroon, "/", "cookie-suffix") return content, 401, headers username = auth_info.identity.username() lp_user = launchpad.people(username) authorized = False for team in AUTHORIZED_TEAMS: if lp_user in launchpad.people(team).members: authorized = True break if not authorized: return ( f"{username} is not in any of the authorized teams: " f"{str(AUTHORIZED_TEAMS)}", 401, ) # Validate authentication token return func(*args, **kwargs)
def do_GET(self): '''do_GET implements a handler for the HTTP GET method''' ctx = checkers.AuthContext() auth_checker = self._bakery.checker.auth( httpbakery.extract_macaroons(self.headers)) try: auth_checker.allow(ctx, [TEST_OP]) except (bakery.PermissionDenied, bakery.VerificationError) as exc: return self._write_discharge_error(exc) self.send_response(200) self.end_headers() content_len = int(self.headers.get('content-length', 0)) content = 'done' if self.path != '/no-body' and content_len > 0: body = self.rfile.read(content_len) content = content + ' ' + body self.wfile.write(content.encode('utf-8')) return
def do_GET(self): '''do_GET implements a handler for the HTTP GET method''' ctx = checkers.AuthContext() auth_checker = self._bakery.checker.auth( httpbakery.extract_macaroons(self.headers)) try: auth_checker.allow(ctx, [TEST_OP]) except (bakery.PermissionDenied, bakery.VerificationError) as exc: return self._write_discharge_error(exc) self.send_response(200) self.end_headers() content_len = int(self.headers.get('content-length', 0)) content = 'done' if self.path != '/no-body'and content_len > 0: body = self.rfile.read(content_len) content = content + ' ' + body self.wfile.write(content.encode('utf-8')) return
def _macaroons_for_domain(cookies, domain): '''Return any macaroons from the given cookie jar that apply to the given domain name.''' req = urllib.request.Request('https://' + domain + '/') cookies.add_cookie_header(req) return httpbakery.extract_macaroons(req)
def _authenticate(request): headers = request.headers macaroons = httpbakery.extract_macaroons(headers) u = bkry.verify(macaroons) return u