Authenticate in Django without a database - python

I have a Django app that gets it's data completely from apis. so I don't have to use database. Session data is stored on signed cookies. I tried to code a custom User model and a custom auth backend like on the docs, but I get the following error:
django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'my_app.MyUser' that has not been installed
My settings.py:
AUTH_USER_MODEL = 'my_app.MyUser'
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
'my_app.backends.LoginAuthBackend',)
models.py:
class MyUser(object):
def save(self):
pass
objects = None
username = ""
Here, If a try use the AbstractUser from django instead of Object I got the following error: AttributeError: 'NoneType' object has no attribute '_meta' or the db table doesn't exit.
backends.py
class LoginAuthBackend(object):
def authenticate(self, username=None, password=None):
if username and password:
try:
response = my_auth_function(username, password)
if response.status_code == 200:
token = response.get('my_key')
user = MyUser()
return user
except MyCustomException:
return None
It's drives me crazy. Looks like Django that's not easy to use without a DB.
EDIT
After several of tries, a simple way to solve this is remove 'django.contrib.auth.backends.ModelBackend' from AUTHENTICATION_BACKENDS and AUTH_USER_MODEL from settings. The model continues basically the same way. works smoothly

The default set of authentication back-end processors is defined in the AUTHENTICATION_BACKENDS setting. See the Django documentation for Customizing authentication.
By default, AUTHENTICATION_BACKENDS is set to:
['django.contrib.auth.backends.ModelBackend']
That’s the basic authentication backend that checks the Django users database and queries the built-in permissions.
So, if you don't want the django.contrib.auth.backends.ModelBackend authentication method, remove that from the list. You'll probably want to find (or create) a different one and add that to the list.

Related

Using Django authentication system with existing hashed passwords

I have a very old Perl CGI system with a table of users. The passwords are stored as computed hashes using the crypt function. I am looking to migrate the system to Django, and I would like to keep the user data while using the Django table structure.
One option would be to copy all the user/password data an auth_user table and use a custom authentication function since I have existing password hashes.
Do I have a better option?
If I go with this option then how can it be implemented?
The documentation for writing up a custom authentication backend are in
Customizing authentication in Django
Since Borodin asked and since you might find it handy to have a sample more specific to your request, I went ahead and wrote up an example that authenticates against a crypt-based file (e.g., htpasswd).
If the backend finds a user with a matching password in the crypt file, it looks for a standard Django user and returns it. If it can't find one it creates one. Obviously you would need to decide how you want to handle the actual details of your implementation.
./authbackend/__init__.py
import crypt
from django.conf import settings
from django.contrib.auth.models import User
class CryptBackend(object):
def authenticate(self, request, username=None, password=None):
crypt_file = getattr(settings, "CRYPT_DB", None)
if crypt_file is None:
return None
password_match = False
with open(crypt_file,"r") as f:
for line in f:
(user, crypted_pass) = line.rstrip().split(":")
if user == username:
password_match = crypt.crypt(password, crypted_pass) == crypted_pass
break
if not password_match:
return None
# found a match in our crypt database
try:
django_user = User.objects.get(username=username)
except User.DoesNotExist:
django_user = User.objects.create_user(username=username, email='', password=password)
django_user.is_staff = True
django_user.save()
return django_user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
That new custom backend is loaded based on additions to settings.py. In my example, I'm keeping the default Django backend and simply adding my new custom one. Django checks them in order, so it will try a standard Django authentication and if that doesn't work moves on to my custom one. The CRYPT_DB parameter is the path to the htpasswd file.
settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'authbackend.CryptBackend',
]
CRYPT_DB = '/path/to/your/passwd.file'
And for completeness, an example of the format (htpasswd) that the above is checking against.
passwd.file
jill:C.1oP2DOot4MY
jack:qJn7lPS/VNssM

Django: How to get domain name inside a signal handler

I'm trying to send an email with my website address from django signal. I found this question: https://stackoverflow.com/a/15521046/2385132 and proceeded as was advised in the accepted answer, but when using that code, I'm getting this error:
AttributeError: 'NoneType' object has no attribute 'get_host'
Which is coming from the get_current_site in my code:
#receiver(post_save, sender=MyModel)
def post_obj_save(sender, instance: MyModel, **kwargs):
def _get_html(obj: MyModel):
return render_to_string('confirmation_email.html', _get_context(obj))
def _get_context(obj: MyModel):
current_site = get_current_site(request=None)
domain = current_site.domain
action = reverse('obj_activation', request=None, format=None, kwargs={})
url = '{protocol}://{domain}/{action}'.format(protocol=PROTOCOL, domain=domain, action=action)
return {
'header': _('Thank you for registering with ASDF.'),
'prompt': _('In order to be able to log in into ASDF administrator panel, you have to activate your account using'),
'link_name': _('this link'),
'activation_url': url
}
send_mail(
_('ASDF account activation'),
_get_html(instance),
EMAIL_FROM,
[obj.owner.email],
fail_silently=False,
)
So the question is: how do I get full url of my view in a signal?
Btw. I'm using django-rest-framework.
In recent Django versions (probably your case), the domain is always taken from the request if SITE_ID is not defined in your settings. See this change introduced in 1.8 Django version:
Changed in Django 1.8:
This function will now lookup the current site based on
request.get_host() if the SITE_ID setting is not defined.
So, in your case request=None you must have the sites framework enabled, an entry for your current site/domain and SITE_ID setting pointing to the right instance in the Site table, try this and you will see :)

request.user not being populated by my django auth backend

I have written a authorization backend class that implements a authenticate method and a get_user method as per the django docs. I've added
AUTHENTICATION_BACKENDS = ('src.lib.auth_backend.MyBackend',)
to my settings.py file. Through print statements I can see that my code is being run and that it is returning a user object of the class I defined with AUTH_USER_MODEL in my settings.py.
By the time I get the request object within my django-rest-framework has_object_permsion function, request.user is always set to AnonymousUser.
Am I missing a step?
I've tried this with and without django.contrib.auth.middleware.AuthenticationMiddleware installed and get the same outcome.
This failure is happening in the following unit test
def test_get_user(self):
client = APIClient() # rest_framework.test.APIClient
client.login(username='user1',password='user1Password')
res = client.get('/websvc/users/' + str(user.user_id) + '/') # request.user will be AnonymousUser
self.assertEqual(res.status_code, 200) # it will be 403, which is appropriate for AnonymousUser
A couple of things may be wrong.
Have you created the user, before runnign the test?
What returns client.login?
Show us the whole test, to tell you what's wrong.

where django store request['user'] in what file

As mentioned in the documentation, authenticated user's object is stored within user variable in templates. i need where django stored user variable in apps file thanks:
user = request.user
request['user'] = user #where is?
thanks for help
It's in the AuthenticationMiddleware.
The official doc mentioned it:
link:
AuthenticationMiddleware associates users with requests using
sessions.
link:
class AuthenticationMiddleware
Adds the user attribute, representing
the currently-logged-in user, to every incoming HttpRequest object.
See Authentication in Web requests.
source code(django.contrib.auth.middleware.py):
class AuthenticationMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
request.user = SimpleLazyObject(lambda: get_user(request))
Make sure you're using RequestContext. Otherwise user is not available in the templates.

Using Flask-Social with Oauth Provider(s) Only, No Local Registration/Login Forms

Is it possible to use Flask-Social and Flask-Security if I only want to use Facebook Login, for example, for user registration and login, i.e. no local registration/login forms?
I looked through the Flask-Social example application and documentation but couldn't tell if this is possible. In the example application, users cannot login with Facebook unless they've previously registered. After registering with the example application, they can associate their Facebook account with their local account.
When I tried to call social.facebook.get_connection() I got an AttributeError 'AnonymousUser' object has no attribute 'id' because there's no current_user, which is defined by flask-security after registration/login.
This is doable without too much extra work using the #login_failed.connect_via decorator. With app as your instance of a Flask app, it would look like
#login_failed.connect_via(app):
def on_login_failed(sender, provider, oauth_response):
connection_values = get_connection_values_from_oauth_response(provider, oauth_response)
ds = current_app.security.datastore
user = ds.create_user( ... ) #fill in relevant stuff here
ds.commit()
connection_values['user_id'] = user.id
connect_handler(connection_values, provider)
login_user(user)
db.commit()
return render_template('success.html')
As for filling in the relevant stuff for creating the user, I just create a random string for the password, and haven't had issues leaving the email null. I also just included the exact same answer on the Flask-Social github page.

Categories

Resources