This is probably essentially a feature request.
I'd like to see pgAdmin 4 support an authentication mechanism where the
username is retrieved from the REMOTE_USER envivonment variable set by
the web server. If the user does not already exist, once should be
created. At no point should the user be prompted with a login form. This
would allow me to integrate with my existing webserver-based login.
Flask already takes REMOTE_USER and exposes it as request.remote_user.
I made several attempts at this, but I just can't quite get it to work.
I'm running on Ubuntu 20.04 with Apache and mod_wsgi. I installed
pgAdmin4 using the .deb/APT packaging from pgadmin.org.
1. I created an authenticate/web.py like this, which ignores the
password and automatically logs in the user with whatever username they
gave, creating them if necessary. This is based off the ldap authentication:
----
"""A blueprint module implementing the webserver authentication."""
import config
from flask_babelex import gettext
from urllib.parse import urlparse
from .internal import BaseAuthentication
from pgadmin.model import User, ServerGroup, db, Role
from flask import current_app
from pgadmin.tools.user_management import create_user
class WebAuthentication(BaseAuthentication):
"""Web Authentication Class"""
def get_friendly_name(self):
return gettext("web")
def authenticate(self, form):
self.username = form.data['email']
self.password = form.data['password']
user_email = None
return self.__auto_create_user(user_email)
def __auto_create_user(self, user_email):
"""Add the web user to the internal SQLite database."""
user = User.query.filter_by(
username=self.username).first()
if user is None:
return create_user({
'username': self.username,
'email': user_email,
'role': 2,
'active': True,
'auth_source': 'web'
})
return True, None
----
That isn't getting the username from request.remote_user, and I'm not
sure where I'd get a request object in that context. More importantly,
the user still sees a login form.
2. I tried adding the auto-create user code as a request_loader, based
on this:
https://flask-login.readthedocs.io/en/latest/#custom-login-using-request-loader
There I would get an infinite loop where the pgadmin4 root would
redirect me to /login which would redirect me back to the root.
I was just hacking this into create_app(). If I got it working, then a
next step would be to investigate a more appropriate way to integrate
this (e.g. as a plugin or something).
3. I tried converting the desktop login code in create_app() to do this,
but that didn't work:
@app.before_request
def before_request():
"""Login the remote user"""
if not current_user.is_authenticated:
if not request.remote_user:
raise Exception("REMOTE_USER not set.")
user = User.query.filter_by(
username=request.remote_user).first()
if user is None:
from pgadmin.tools.user_management import create_user
user = create_user({
'username': request.remote_user,
'email': None,
'role': 2,
'active': True,
'auth_source': 'internal'
})
login_user(user)
I tried just running in desktop mode (despite the warnings against
that), but that didn't seem to work either. I get a bunch of
INTERNAL_SERVER_ERROR and the interface does not fully load.
Any thoughts?
--
Richard