How to set HTTP password in gerrit while making a REST call using python-requests? - rest

Using requests module want to query Gerrit server and for this need set authentication in below code. And this needs to be done without using any other module like pygerrit2. And if this can be done using HTTP password generated from gerrit. If this is not doable, share an example if auth has to be passed along with Get request.
import requests
import json
import os
GERRIT_SERVER = 'https://gerrit.abc.com'
class GerritServer():
def __init__(self):
self._base_url = GERRIT_SERVER
self._endpoints = {
'CHANGES': 'changes',
}
def _get_endpoint(self, token):
try:
endpoint = self._endpoints[token]
except ValueError:
raise Exception('endpoint not defined for {0}'.format(token))
return '{0}/{1}'.format(self._base_url, endpoint)
def get_endpoint_changes(self):
return self._get_endpoint('CHANGES')

Gerrit's REST API uses digest auth.
username = 'foo'
httpassword = 'bar'
api = 'baz'
auth = requests.auth.HTTPDigestAuth(username, httpassword)
r = requests.get(api, auth=auth)

Related

authlib.jose.errors.InvalidClaimError: invalid_claim: Invalid claim "iss"

I'm building an oauth2 client with Flask and Authlib. My code to register the oauth is:
google = oauth.register(
name='google',
client_id='',
client_secret="",
access_token_url="https://accounts.google.com/o/oauth2/token",
access_token_params=None,
authorize_url="https://accounts.google.com/o/oauth2/auth",
authorize_params=None,
api_base_url="https://www.googleapis.com/oauth2/v1/",
client_kwargs={'scope': 'openid email'},
server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
)
And my /authorize endpoint looks like this:
#app.route('/authorize')
def authorize():
google = oauth.create_client('google')
token = google.authorize_access_token()
resp = google.get('userinfo')
resp.raise_for_status()
userinfo = resp.json()
return str(userinfo)
But I am getting the error
authlib.jose.errors.InvalidClaimError: invalid_claim: Invalid claim "iss"
I had this issue and removing the openid value from scope fixed it. I guess my google config didn't accomodate it,

FastAPI auth check before granting access to sub-applications

I am mounting a Flask app as a sub-application in my root FastAPI app, as explained in the documentation
Now I want to add an authentication layer using HTTPAuthorizationCredentials dependency, as nicely explained in this tutorial
tutorial code
How can I do that?
Preferably, I would like that any type of access attempt to my Flask sub-application goes first through a valid token authentication process implemented in my FastAPI root app. Is that possible?
You can use a custom WSGIMiddleware and authorize the call to flask app inside that like this:
from fastapi import FastAPI, Depends, HTTPException
from fastapi.middleware.wsgi import WSGIMiddleware
from flask import Flask, escape, request
from starlette.routing import Mount
from starlette.types import Scope, Receive, Send
flask_app = Flask(__name__)
def authenticate(authorization: str = Header()):
# Add logic to authorize user
if authorization == "VALID_TOKEN":
return
else:
raise HTTPException(status_code=401, detail="Not Authorized")
class AuthWSGIMiddleware(WSGIMiddleware):
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
_, authorization = next((header for header in scope['headers'] if header[0] == b'authorization'), (b'authorization', "" ))
authenticate(authorization.decode('utf-8'))
await super().__call__(scope, receive, send)
routes = [
Mount("/v1", AuthWSGIMiddleware(flask_app)),
]
# OR Optionally use this as you were doing
# The above one is preferred as per starlette docs
# app.mount("/v1", WSGIMiddleware(flask_app))
#flask_app.route("/")
def flask_main():
name = request.args.get("name", "World")
return f"Hello, {escape(name)} from Flask!"
app = FastAPI(routes=routes, dependencies=[Depends(authenticate)])
#app.get("/v2")
def read_main():
return {"message": "Hello World"}
For the tutorial you are looking at, you can replace the invoking authenticate function in my example inside AuthWSGIMiddleware->__call__() with
AuthHandler().decode_toke(authorization)

Flask OIDC with Keycloak returning None when trying to get 'openid_id' and 'role' fields

I'm trying to build some test applications using Flask-ODBC + Keycloak.
I successfully started Keycloak, created a Realm, a role and an user to who this role is assigned. Now I'm trying to get the 'role' and 'openid_id' fields from the OpenIDConnect object but these two fields are returning None.
My application code is the following
import json
import flask
from flask import Flask, render_template, g
import flask_login
from flask_login import login_required
from flask_oidc import OpenIDConnect
from model.user import User
app = Flask(__name__)
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
# Our mock database.
users = {'foo#bar.com': {'pw': 'secret'}}
app.config.update({
'SECRET_KEY': 'u\x91\xcf\xfa\x0c\xb9\x95\xe3t\xba2K\x7f\xfd\xca\xa3\x9f\x90\x88\xb8\xee\xa4\xd6\xe4',
'TESTING': True,
'DEBUG': True,
'OIDC_CLIENT_SECRETS': 'client_secrets.json',
'OIDC_ID_TOKEN_COOKIE_SECURE': False,
'OIDC_REQUIRE_VERIFIED_EMAIL': False,
'OIDC_VALID_ISSUERS': ['http://localhost:8080/auth/realms/MyDemo'],
'OIDC_OPENID_REALM': 'http://localhost:5000/oidc_callback'
})
oidc = OpenIDConnect(app)
#app.route('/')
def hello_world():
if oidc.user_loggedin:
return ('Hello, %s, See private '
'Log out') % \
oidc.user_getfield('email')
else:
return 'Welcome anonymous, Log in'
#app.route('/private')
#oidc.require_login
def hello_me():
info = oidc.user_getinfo(['email', 'openid_id', 'role'])
print(info)
return ('Hello, %s (%s)! Return' %
(info.get('email'), info.get('openid_id')))
#app.route('/api')
#oidc.accept_token(True, ['openid'])
def hello_api():
return json.dumps({'hello': 'Welcome %s' % g.oidc_token_info['sub']})
#app.route('/logout')
def logout():
oidc.logout()
return 'Hi, you have been logged out! Return'
if __name__ == '__main__':
app.run('localhost', port=5000)
Inside hello_me() I try to get the 'openid_id' and 'role' and print it. The problem is I'm getting None in these fields. I can get the email correctly.
Can you help me with finding out what mistakes I'm making?
https://flask-oidc.readthedocs.io/en/latest/
user_getinfo(fields, access_token=None)
Returns: The values of the current user for the fields requested. The keys are the field names, values are the values of the fields as indicated by the OpenID Provider. Note that fields that were not provided by the Provider are absent.
Your provider (Keycloak) doesn't expose openid_id details in the token apparently, so field is absent. Very likely you didn't configure OIDC client in the Keycloak correctly. Make sure you have added correct mappers/scopes to used OIDC client in the Keycloak, which expose requested details into openid_id claim.

What's going wrong when I try to create a review comment through Github's v3 API?

I'm trying to create a review commit through Github's v3 API and am not succeeding. Consider this repository. There's a single pull request and for the purposes of this question let's say I want to leave a 'changes requested' review on that PR. Here's the code I've got:
#!/usr/bin/env python3
import requests
import json
TOKEN='YOUR_TOKEN_HERE'
REPO = "blt/experiment-repo"
PR_NUM = 1
COMMIT_SHA_1 = "4160bee478c3c985eaaa35f161cc922fe20b354a"
COMMIT_SHA_2 = "df9d13a2e35f9b6c228e1f30ea30585ed85af26a"
def main():
pr_comment_headers = {
'user-agent': 'benedikt/0.0.1',
'Authorization': 'token %s' % TOKEN,
# Accept header per
# https://developer.github.com/changes/2016-12-16-review-requests-api/
'Accept': 'application/vnd.github.black-cat-preview+json',
}
msg = "BLEEP BLOOP I AM A ROBOT"
payload = { 'commit_id': COMMIT_SHA_2,
'body': msg,
'event': "REQUEST_CHANGES" }
# Per https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review
review_url = "https://api.github.com/repos/%s/pulls/%s/reviews" % (REPO, PR_NUM)
res = requests.post(review_url, headers = pr_comment_headers,
json = json.dumps(payload))
print(res)
print(res.text)
if __name__ == '__main__':
main()
I've marked in code comments where I've discovered the API endpoints to hit and with what payloads. Excepting, I must have goofed somewhere because when I run the above program I receive:
<Response [422]>
{"message":"Validation Failed","errors":["Variable commitOID of type GitObjectID was provided invalid value","Variable event of type PullRequestReviewEvent was provided invalid value"],"documentation_url":"https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review"}
I've verified that the commit SHAs are the exact ones that Github shows and REQUEST_CHANGES is the string in the documentation.
What am I missing?
I think you need to let requests encode the request body instead of encoding it yourself with json.dumps(), something like this: requests.post(..., json=payload)

How to log in to a website with urllib?

I am trying to log on this website: http://www.broadinstitute.org/cmap/index.jsp. I am using python 3.3 on Windows. I followed this answer https://stackoverflow.com/a/2910487/651779. My code:
import http.cookiejar
import urllib
url = 'http://www.broadinstitute.org/cmap/index.jsp'
values = {'j_username' : 'username',
'j_password' : 'password'}
data = urllib.parse.urlencode(values)
binary_data = data.encode('ascii')
cookies = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(
urllib.request.HTTPRedirectHandler(),
urllib.request.HTTPHandler(debuglevel=0),
urllib.request.HTTPSHandler(debuglevel=0),
urllib.request.HTTPCookieProcessor(cookies))
response = opener.open(url, binary_data)
the_page = response.read()
http_headers = response.info()
It runs without erros, however the html in the_page is just the log in page. How can I log onto this page?
The site is using a JSESSIONID cookie to create session since HTTP requests are stateless. When you're making your request, you're not getting that session id first.
I sniffed a session to log into that site using Fiddler and found that the POST is made to a different URL, but it has that JSESSIONID cookie set. So you need to make a get to the URL first, capture that cookie using the cookiehandler, then POST to this URL:
post_url = 'http://www.broadinstitute.org/cmap/j_security_check'
You don't need to save the HTTP GET request at all, you can simply call opener.open(url), then in your code change the response line to this:
response = opener.open(post_url, binary_data)
Also the payload was missing the submit method. Here's the whole thing with the changes I suggest:
import http.cookiejar
import urllib
get_url = 'http://www.broadinstitute.org/cmap/index.jsp'
post_url = 'http://www.broadinstitute.org/cmap/j_security_check'
values = urllib.parse.urlencode({'j_username': <MYCOOLUSERNAME>,
'j_password': <MYCOOLPASSSWORD>,
'submit': 'sign in'})
payload = bytes(values, 'ascii')
cj = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(
urllib.request.HTTPRedirectHandler(),
urllib.request.HTTPHandler(debuglevel=0),
urllib.request.HTTPSHandler(debuglevel=0),
urllib.request.HTTPCookieProcessor(cj))
opener.open(get_url) #First call to capture the JSESSIONID
resp = opener.open(post_url, payload)
resp_html = resp.read()
resp_headers = resp.info()
Any other requests using the opener you created will re-use that cookie and you should be able to freely navigate the site.