Web Server Authentication Integration (REMOTE_USER)

From: Richard Laager <rlaager(at)wiktel(dot)com>
To: pgadmin-support(at)lists(dot)postgresql(dot)org
Subject: Web Server Authentication Integration (REMOTE_USER)
Date: 2020-08-14 06:19:15
Message-ID: 6d834f81-5d96-b974-295a-8308ef3170b1@wiktel.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgadmin-support

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

Browse pgadmin-support by date

  From Date Subject
Next Message Patrice LACHANCE 2020-08-14 11:56:09 Re: PGAdmin integration with IAM authentication
Previous Message Andrew Sanderson 2020-08-13 18:39:57 User profile location redirect