Django Auth with LDAP and Local Accounts - python

I currently have Django and LDAP working. However, I want to limit LDAP authentication to only the accounts within the local account DB.
i.e if no local account is present deny access/ldap auth occurring.
From looking at the options from LDAPSearch I'm unable to find a direct option to provide this. Any ideas on how to achieve this?
Limiting based on LDAP OU is not an option based on the LDAP address structure in place.
Thanks,

As felix001 pointed out in the comments, the project documentation describes a flag AUTH_LDAP_NO_NEW_USERS:
Prevent the creation of new users during authentication. Any users not already in the Django user database will not be able to login.
Enabling it in the settings turns the desired behaviour on:
# settings.py
...
AUTH_LDAP_NO_NEW_USERS = True
However, this feature is not contained in the currently released 1.7.0 version, so a backport is necessary. The least invasive impl I can think about is:
# myapp/backend.py
from django_auth_ldap import backend as ldap_backend
# backport for AUTH_LDAP_NO_NEW_USERS setting
ldap_backend.LDAPSettings.defaults.update(NO_NEW_USERS=False)
class MyLDAPBackend(ldap_backend.LDAPBackend):
def get_or_build_user(self, username, ldap_user):
user, built = super().get_or_build_user(username, ldap_user)
if self.settings.NO_NEW_USERS and built: # user was not found in local db and created instead
raise ldap_user.AuthenticationFailed(
f'username {username} does not exist in local DB.'
)
return user, built
Activate AUTH_LDAP_NO_NEW_USERS in settings and use the custom backend instead of the LDAPBackend for now:
# myapp/settings.py
AUTH_LDAP_NO_NEW_USERS = True
AUTHENTICATION_BACKENDS += ('myapp.backend.MyLDAPBackend',)
Dropping the backport is also easy: once the next version of django-auth-ldap is released, the only thing you will need to adapt is the AUTHENTICATION_BACKENDS tuple (and of course, remove the myapp/backend.py module).

Related

How to set the two types of authentication for airflow: AUTH_LDAP and AUTH_DB? If airflow can't find an user in AUTH_LDAP it has to search in AUTH_DB

I would like to set up two types of authentification in the Airflow webserver. The first preferred type is LDAP. If airflow doesn't find a user in LDAP it has to try to find credentials for the user in airflow_db (default authentification method for airflow).
First I try to set several auth methods in webserver_config.py:
from flask_appbuilder.security.manager import AUTH_LDAP
from airflow.www.fab_security.manager import AUTH_DB
AUTH_TYPE = [AUTH_LDAP, AUTH_DB]
But It didn't work.
After I tried to change auth_backend in airflow.cfg
auth_backend = airflow.contrib.auth.backends.ldap_auth.LDAPAuthBackend,airflow.contrib.auth.backends.password_auth.PasswordUser
But it didn't work too.
Are there any methods for setting several types of authentification in the airflow webserver?
In short, no. You can only configure a single authentication type.
Airflow leverages Flask-AppBuilder for authentication. Two notes:
OAuth authentication does allow for configuring multiple OAuth providers (e.g. Google and Okta), see https://flask-appbuilder.readthedocs.io/en/latest/security.html#authentication-oauth.
A similar question was asked here: How to support both OAuth and Basic Auth in FlaskAppBuilder in Superset. You could technically subclass FAB's SecurityManager to implement your own behavior.
Also, see this GitHub issue requesting the same: https://github.com/dpgaspar/Flask-AppBuilder/issues/1803.

How to list available authentication backends for a Django user?

I have a project that uses Python 3.6 and Django 1.11 where I use the built-in User model.
The user objects are all inside the default database (which is postgres), but the project uses a second authentication backend because some users need to be authenticated against a legacy Oracle database.
# settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend', # new postgres DB
'project_config.auth_backends.OtherBackend', # legacy Oracle DB
]
This works fine so far, but now I have 3 groups of users:
some users can only authenticate in ModelBackend because they are not in the legacy DB (because they are new users).
some users can only authenticate in the legacy DB; they have usr.has_usable_password() == False because they have not set their password in the new postgres DB yet.
some users can authenticate in both backends, maybe even with different passwords in each one; this is because they changed their password in the new system, but by design that change is not transmitted back to the legacy DB (don't shoot me, the only way to change the password in the legacy DB is to do it manually through the user interface).
For auditing purposes, I want to list all users and see which backends each one has available (ignoring the is_active flag for now) to make auditing tasks easier.
My idea was to use a loop similar to this one:
for usr in User.objects.all():
backend_list = []
if usr.has_usable_password():
backend_list.append('ModelBackend')
if ... : # what should I check here ?
backend_list.append('OtherBackend')
print(usr, backend_list)
I don't have the passwords for each user for the legacy database, so is may idea even possible?
I have not found a way, but I am open to suggestions.
In the end, I had to go with the suggestion from #ivissani and query the users table in the legacy Oracle DB:
select * from all_users;
With this information at hand, I could compare it to the users in the postgres DB and work out which users appear only in one or in both.

ArangoDB with Python - 401 not authorized to execute this request

I am currently trying to create and populate a collection automatically via Python. As mentioned the DB that I've chosen is Arango. The issue that I am having is that I get an error:
[HTTP 401][ERR 11] not authorized to execute this request
I've turned off fire walls and tried reinstalling Arango to check if I messed up something with setting up the admin profile but everything seems to be fine. The only similar question I've found so far is this one but it's still somehow irrelevant to my issue.
In regards to the builder, this is how I've set it up:
config={
"database": 'exampleDB',
"host":'localhost',
"protocol":'http',
"port":'8529',
"username":'someone',
"password":'xxxx'
}
Is there anything else that I am missing for setting up the permissions config? Or the issue might be somewhere else.
I am not posting my full code because it's company owned software.
In case of pyArango you need to provide the credentials when you instantiate the Connection class:
from pyArango.connection import *
conn = Connection(username=config['username'], password=config['password'])
Documentation: https://bioinfo.iric.ca/~daoudat/pyArango/connection.html
In case of python-arango you first need a client object, then call the db() method and provide the database name and the credentials:
from arango import ArangoClient
client = ArangoClient(protocol='http', host='localhost', port=8529)
db = client.db('_system', username=config['username'], password=config['password'])
Documentation: https://python-driver-for-arangodb.readthedocs.io/en/master/overview.html
The original repository of arango-python is abandoned since five years, it does not support any 2.x or 3.x server versions and I could not find anything for authentication (search on GitHub yielded no results for "auth", "password", "user", "cred"). The docs site is even more stale.
The ArangoPy project appears to be pretty dead as well, it supports ArangoDB 2.2 through 2.6 according to the readme. There is no example given for authentication. The Client constructor does take a parameter called auth, but the usage remains unclear (<username>:<password>?).

Flask: share sessions between domain.com and username.domain.com

I have a flask running at domain.com
I also have another flask instance on another server running at username.domain.com
Normally user logs in through domain.com
However, for paying users they are suppose to login at username.domain.com
Using flask, how can I make sure that sessions are shared between domain.com and username.domain.com while ensuring that they will only have access to the specifically matching username.domain.com ?
I am concerned about security here.
EDIT:
Later, after reading your full question I noticed the original answer is not what you're looking for.
I've left the original at the bottom of this answer for Googlers, but the revised version is below.
Cookies are automatically sent to subdomains on a domain (in most modern browsers the domain name must contain a period (indicating a TLD) for this behavior to occur). The authentication will need to happen as a pre-processor, and your session will need to be managed from a centralised source. Let's walk through it.
To confirm, I'll proceed assuming (from what you've told me) your setup is as follows:
SERVER 1:
Flask app for domain.com
SERVER 2:
Flask app for user profiles at username.domain.com
A problem that first must be overcome is storing the sessions in a location that is accessible to both servers. Since by default sessions are stored on disk (and both servers obviously don't share the same hard drive), we'll need to do some modifications to both the existing setup and the new Flask app for user profiles.
Step one is to choose where to store your sessions, a database powered by a DBMS such as MySQL, Postgres, etc. is a common choice, but people also often choose to put them somewhere more ephemeral such as Memcachd or Redis for example.
The short version for choosing between these two starkly different systems breaks down to the following:
Database
Databases are readily available
It's likely you already have a database implemented
Developers usually have a pre-existing knowledge of their chosen database
Memory (Redis/Memchachd/etc.)
Considerably faster
Systems often offer basic self-management of data
Doesn't incur extra load on existing database
You can find some examples database sessions in flask here and here.
While Redis would be more difficult to setup depending on each users level of experience, it would be the option I recommend. You can see an example of doing this here.
The rest I think is covered in the original answer, part of which demonstrates the matching of username to database record (the larger code block).
Old solution for a single Flask app
Firstly, you'll have to setup Flask to handle subdomains, this is as easy as specifying a new variable name in your config file. For example, if your domain was example.com you would append the following to your Flask configuration.
SERVER_NAME = "example.com"
You can read more about this option here.
Something quick here to note is that this will be extremely difficult (if not impossible) to test if you're just working off of localhost. As mentioned above, browsers often won't bother to send cookies to subdomains of a domain without dots in the name (a TLD). Localhost also isn't set up to allow subdomains by default in many operating systems. There are ways to do this like defining your own DNS entries that you can look into (/etc/hosts on *UNIX, %system32%/etc/hosts on Windows).
Once you've got your config ready, you'll need to define a Blueprint for a subdomain wildard.
This is done pretty easily:
from flask import Blueprint
from flask.ext.login import current_user
# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")
# Define our route
#deep_blue.route('/')
def user_index(username):
if not current_user.is_authenticated():
# The user needs to log in
return "Please log in"
elif username != current_user.username:
# This is not the correct user.
return "Unauthorized"
# It's the right user!
return "Welcome back!"
The trick here is to make sure the __repr__ for your user object includes a username key. For eg...
class User(db.Model):
username = db.Column(db.String)
def __repr__(self):
return "<User {self.id}, username={self.username}>".format(self=self)
Something to note though is the problem that arises when a username contains special characters (a space, #, ?, etc.) that don't work in a URL. For this you'll need to either enforce restrictions on the username, or properly escape the name first and unescape it when validating it.
If you've got any questions or requests, please ask. Did this during my coffee break so it was a bit rushed.
You can do this with the builtin Flask sessions, which are cookie-based client-side sessions. To allow users to login to multiple subdomains in '.domain.com', you need only to specify
app.config['SESSION_COOKIE_DOMAIN'] = '.domain.com'
and the client's browser will have a session cookie that allows him to login to every Flask instance that is at 'domain.com'.
This only works if every instance of Flask has the same app.secret_key
For more information, also see
Same Flask login session across two applications

Trac Using Database Authentication

Is it possible to use a database for authentication with Trac?
.htpasswd auth is not desired in this install.
Using Trac .11 and MySQL as the database. Trac is currently using the database, but provides no authentication.
Out of the box, Trac doesn't actually do its own authentication, it leaves it up to the web server. So, you've got a wealth of Apache-related options available to you. You could maybe look at something like auth_mysql to let you keep user credentials in a database.
Alternatively, take a look at the AccountManagerPlugin on trac-hacks.org
You can use Account Manager Plugin with SessionStore
The AccountManagerPlugin offers several features for managing user accounts:
allow users to register new accounts
login via an HTML form instead of using HTTP authentication
allow existing users to change their passwords or delete their accounts
send a new password to users who’ve forgotten their password
administration of user accounts
Please refer to http://trac-hacks.org/wiki/AccountManagerPlugin
Do the following on your trac.ini
[components]
; be sure to enable the component
acct_mgr.svnserve.* = enabled
acct_mgr.svnserve.svnservepasswordstore = enabled
; choose one of the hash methods
acct_mgr.pwhash.htdigesthashmethod = enabled
acct_mgr.pwhash.htpasswdhashmethod = enabled
[account-manager]
password_store = SvnServePasswordStore
password_file = /path/to/svn/repos/conf/passwd
; choose one of the hash methods
hash_method = HtDigestHashMethod
hash_method = HtPasswdHashMethod
Now trac will use user in the database

Categories

Resources