Django one user across multiple django sites - python

I have a case where I have three sites A, B, C . A is more of administrative site, B and C are consumer facing applications. Each of them have their own separate databases. What I want to do now is as follows:
I want all three A, B, C to be connected in such a way that when a new user registers at B and when registration is successful, the same user should be able to login into A or C. If there is any change in users details they reflect on A and C as well.
What I thought as solution till now is:
When a user registers at B, I can replicate user on A and C with same credentials, so this would allow user to login into any of the three sites.
I was also pondering upon idea of having a central database to store django auth models, but I am not sure in that case how authentication would work.
I need suggestion as to how this can be done. Also, does this qualify to be called as SSO?
In case if someone finds question to be misleading or vague or inappropriate do mention as to why before downvoting

There are two solutions to your problem:
Use routing to multiple databases. Django supports different databases for different models (more info on # https://docs.djangoproject.com/en/1.8/topics/db/multi-db/). So you could route all queries for your auth models to the authentication database. Django documentation already provides such a configuration # https://docs.djangoproject.com/en/1.8/topics/db/multi-db/#an-example. I've not tested this configuration however this will have the problem of not being able to use foreign keys to your user model (since it will be stored in a different database). To resolve this, you could have a UserProxy model (or something similarly named) in all your projects that will just keep the username of the User so you'll be able to create foreign key relations from your models to the UserProxy. Any user data that would need to be retrieved would be forwarded to the correct User object by the UserProxy.
Use django authentication. Django can be configured to use a number of different authentication methods (check the docs # https://docs.djangoproject.com/en/1.8/topics/auth/customizing/). For your sites B and C you can configure an authentication backend that uses the database of the administrative site (A) to login. I am using this method succesfully - here how you could do it:
class RegUsrBackend(django.contrib.auth.backends.ModelBackend):
def authenticate(self, username=None, password=None):
try:
conn = django.db.connections['users']
cursor = conn.cursor()
cursor.execute("select pn.password, pn.username, au.first_name, au.last_name from auth_user au where au.username = %s ", [username])
row = cursor.fetchone()
if row and check_password(password, row[0]):
user, created = get_user_model().objects.get_or_create(username = row[1] )
if user.first_name != row[2] or user.last_name != row[3] :
user.first_name = row[2]
user.last_name = row[3]
user.set_unusable_password()
user.save()
return user
except:
return None
As you can see, here I've also configured a different users database (named users) and I am issuing a raw query to this database to get the user with the passed username and its password hash. After that, I check if the user exists and has the correct password (using the check_password) and if everything checks, I use get_or_create to either retrieve or create the local instance (i.e the instance of the user in the application's database) of that user. Finally, before returning the user I check to see if there's a change in his first or last name to the administrative application and update them in the local one.
Finally, you need to put this backend in the in the AUTHENTICATION_BACKENDS settings of the applications using it.
Using this method you won't have to use any UserProxy to support foreign keys (since there will exist a local User model) models but I feel that it is a more hackish method than the first one. Also, if the details of a user has been changed in the administrative database the details in the others will be updated when he logs in. Also you could change your backend to something even more exotic, for example instead of querying the database you could create a REST api in your administrative backend and use this to login to the other applications.
Finally, to answer your question about SSO, what I've described above is not SSO. SSO means that when a user logs in to a site he won't have to log in again to the others becase a single session key is kept and shared in all these sites. A common solution for SSO is to use CAS (http://jasig.github.io/cas/4.1.x/index.html) however I don't have good experience with it (and you'll need to have another, java based server to host CAS).

Related

Django different USERNAME field for registration and for login

I want to have a USERNAME_FIELD for registration of my users (lets say this field is the country ID) and another one for login (internal id).
My process is as follows:
User registers with name, email and COUNTRY_ID (as username_field) aswell with a password.
Next, an admin validates this information and confirms the registration of this user and an automatic internal ID is assigned.
The user is then informed (via email) of this action and can therefore login with this internal id.
Is it possible then to have a different USERNAME_FIELD for registration and for login?
The username is supposed to be unique (obviously - how else would you identify your user at login ?), so in it's current formulation, your question doesn't really make sense. If you don't want the users to provide a username when applying for registration, then your question is not about having "a USERNAME_FIELD for registration and another for login", it's about "how to allow the user to register without providing a value for the USERNAME_FIELD".
One possible solution is to use a system-provided unique random value (ie an uuid or something built from user's ip + current timestamp + some random value) as temporary username.
Another is to use a distinct model for registrations and only create the User during validation process (which, if you're using the default User model, will help telling the yet unvalidated registrations).
In both cases you'll of course have to write your own forms and views (preferably delegating what can be to the contrib.auth app - don't try to reinvent the wheel either).

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.

Creating Users & Sending Password Resets in Django

I apologize if there has been a substantial answer on this already, I have searched for quite a while and can't find any helpful answers.
I have a django project that has varying levels of account access. I have a group of 'Managers' and I want to allow them to manage user accounts.
I want the Managers to be able to create the user account objects for the users. However, I don't want them to have to deal with creating their passwords and sending them to the users (for obvious reasons). This way, managers can impersonate users to get work done on their behalf, and users maintain password control and ownership of their data.
The idea is that managers create the account, and when the account is created Users will be sent a password reset form (same as django's out-of-box auth) which will allow them to set their passwords.
My code looks similar to below (omitted non-imperative stuff)
from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordResetForm
#manager_required
def manager_add_users(request):
add_user_form = manager_add_user_form(request.POST)
new_user_name = add_user_form.cleaned_data['user_name']
new_user_email = add_user_form.cleaned_data['user_email']
new_user = User.objects.create_user(
username = new_user_name,
email = new_user_email,
)
new_user.save()
set_password_form = PasswordResetForm({'email': new_user.email })
if set_password_form.is_valid():
print 'Reset Form Is Valid'
set_password_form.save(
request= request,
use_https=True,
from_email="support#org.com",
email_template_name='registration/password_reset_email.html')
The account creates properly and everything runs without error. The issue is that although the form is valid (reset form is valid statement prints) it is not actually sending the reset form. There are no form errors.
However, in testing when I initialize the form with an address that already exists in the system like this:
set_password_form = PasswordResetForm({'email':'existing_address#example.com'})
The password reset form sends without error. So it only works with existing user email addresses in the system, but although the user has been created and the .save() method called, it's still not catching it (The users are marked as 'Active' upon creation, so they should be able to be found)
I'm a bit at a loss. I can think of a few ways that I could get around this issue, but this seems the most direct way of doing it and I'm really not entirely sure why it doesn't work.
Yes, I am able to send messages. I am using django's backend mail for testing:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Glancing at the source code, it looks like Django is ignoring the request because the password is blank.
Try setting a temporary password (using, say, django.utils.crypto.get_random_string()) before saving the user.

Do I need to re-implement authentication in Mongo?

My Flask application is currently uses PostgreSQL to store all authentication information (users, tokens) and interact with it. For other logic I need to use MongoDB. I like to use one technologies of one type instead of multiple to reduce complexity of an application (for example, only Redis instead of Redis + memcached).
So for now I'm thinking about using MongoDB only, using it as backend for authentication process.
Current workflow is following: PostgreSQL stores two tables: User and Token. When I sign up user, I open transaction, store his data (username, login, password) in User table, then insert activation token to Token table, then send activation letter, then close transaction. So, problem occurred in storing User, Token or later in code, when I'll try to send email, transaction will be rolled back. It prevents cases when user created, but token not, so account can't be activated.
As I know, transactions is not feature of MongoDB. So, if I will have two documents, User and Token, I will not be able to rollback creating of first if second can't be created.
My questions are:
How would you implement described behavior on MongoDB?
Is it good idea to use only MongoDB for all stuff instead of PostgreSQL for authentication and MongoDB for documents?
Yes, you do have to implement the signup logic yourself in this case. For example if you store the following document:
user : {
name : 'Alex Black'
email : 'alex#example.com'
token : {
value : 'some_random_token' # subject for indexing
expires : '00.00.00.18.11.2012' # use MongoDB date here
}
}
Then e.g. a cron script should remove all the expired users. But a trick would be in using the NoSQL features of Mongo!
Just create two separate collections: Users and e.g. UnregisteredUsers. Store the user information to UnregisteredUsers and only when registration is confirmed transfer the user document from UnregisteredUsers to Users.

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