django-publicauth / publicauth / backends / __init__.py

from django.conf import settings as global_settings
from django.contrib import messages
from django.contrib import auth

from annoying.exceptions import Redirect

from publicauth.models import PublicID
from publicauth.utils import str_to_class
from publicauth import settings
from publicauth import lang


class BaseBackend(object):

    PROFILE_MAPPING = property(lambda self: getattr(global_settings, "%s_PROFILE_MAPPING" % self.provider.upper(), {}))

    def __init__(self, provider):
        self.provider = provider
        self.identity = None

    def begin(self, request, data):
        raise NotImplementedError

    def complete(self, request, response):
        raise NotImplementedError

    def validate(self, request, data):
        raise NotImplementedError

    def get_extra_data(self, response):
        raise NotImplementedError
    
    def merge_accounts(self, request):
        """
        Attach PublicID account to regular django 
        account and then redirect user. In this situation
        user dont have to fill extra fields because he filled
        them when first account (request.user) was created.

        Note that self.indentity must be already set in this stage by
        validate_response function. 
        """
        # create new public ID record in database
        # and attach it to request.user account.
        publicid = PublicID()
        publicid.user = request.user 
        publicid.identity = self.identity 
        publicid.provider = self.provider
        publicid.save()

        # show nice message to user.
        messages.add_message(request, messages.SUCCESS, lang.ACCOUNTS_MERGED)
        # redirect user.
        raise Redirect(global_settings.LOGIN_REDIRECT_URL)

    def login_user(self, request):
        """
        Try to login user by public identity.
        Do nothing in case of failure.
        """
        # only actavted users can login if activation required.
        if settings.PUBLICAUTH_ACTIVATION_REQUIRED and not request.user.is_active:
            messages.add_message(request, messages.ERROR, lang.NOT_ACTIVATED)
            raise Redirect(global_settings.LOGIN_REDIRECT_URL)

        # authenticate and redirect user.
        user = auth.authenticate(identity=self.identity, provider=self.provider)
        if user:
            messages.add_message(request, messages.SUCCESS, lang.SUCCESSFULLY_AUTHENTICATED)
            auth.login(request, user)

            try:
                redirect_url = request.session['next_url']
                del request.session['next_url']
            except KeyError:
                redirect_url = global_settings.LOGIN_REDIRECT_URL
            raise Redirect(redirect_url)

    def fill_extra_fields(self, request, extra):
        """
        Try to fetch extra data from provider, if this data is enough
        to validate settings.EXTRA_FORM then call save method of form 
        class and login the user.

        The extra parameter can be some complex object
        this is why we use method function 'extra_data' to
        extract data from this object.

        Also we need to create a dictionary with remapped 
        keys from profile mapping settings.
        """
        data = {}
        for field in self.PROFILE_MAPPING:
            data.update(self.extract_data(extra, field))

        form = str_to_class(settings.EXTRA_FORM)(data, fields=self.PROFILE_MAPPING)
        if form.is_valid():
            form.save(self.identity, self.provider)
            self.login_user(request)
        else:
            return data

    def extract_data(self, extra, field):
        """
        If extra isnt standart python dictionary
        you need to implement this method to retrive
        values from this object.
        """
        try:
            return {self.PROFILE_MAPPING[field][0]: extra.get(field, '')}
        except AttributeError:
            return {self.PROFILE_MAPPING[field][0]: ''}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.