Custom Search

Thursday, January 16, 2014

Openstack Horizon and Django Compare the Working of authentication (Login)

Working of authentication (Login) in django
==============================================

1)
site-packages/django/contrib/auth/forms.py

from django.contrib.auth import authenticate

class AuthenticationForm(forms.Form):
    def clean(self):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')

        if username and password:
            self.user_cache = authenticate(username=username,
                                           password=password)


2)
./openstack_dashboard/settings.py

AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)

3)
site-packages/django/contrib/auth/__init__.py

def get_backends():
    from django.conf import settings
    backends = []
    for backend_path in settings.AUTHENTICATION_BACKENDS:
        backends.append(load_backend(backend_path))

    if not backends:
        raise ImproperlyConfigured('No authentication backends have been defined. Does AUTHENTICATION_BACKENDS contain anything?')
    return backends

def authenticate(**credentials):
    """
    If the given credentials are valid, return a User object.
    """
    for backend in get_backends():
        try:
            user = backend.authenticate(**credentials)
        except TypeError:
            # This backend doesn't accept these credentials as arguments. Try the next one.
            continue
        if user is None:
            continue
        # Annotate the user object with the path of the backend.
        user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
        return user

    # The credentials supplied are invalid to all backends, fire signal
    user_login_failed.send(sender=__name__,
            credentials=_clean_credentials(credentials))


Working of authentication (Login) in Horizon
==============================================

1a)
horizon/openstack_dashboard/views.py

from openstack_auth import views

@vary.vary_on_cookie
def splash(request):
    if request.user.is_authenticated():
        return shortcuts.redirect(get_user_home(request.user))
    form = views.Login(request)
    request.session.clear()
    request.session.set_test_cookie()
    return shortcuts.render(request, 'splash.html', {'form': form})

1b)
site-packages/openstack_auth/views.py

from django.contrib.auth.views import login as django_login
from .forms import Login

def login(request):
    initial = {}
    current_region = request.session.get('region_endpoint', None)
    requested_region = request.GET.get('region', None)
    regions = dict(getattr(settings, "AVAILABLE_REGIONS", []))
    if requested_region in regions and requested_region != current_region:
        initial.update({'region': requested_region})

    if request.method == "POST":
        form = curry(Login, request)
    else:
        form = curry(Login, initial=initial)


    extra_context = {'redirect_field_name': REDIRECT_FIELD_NAME}

    if request.is_ajax():
        template_name = 'auth/_login.html'
        extra_context['hide'] = True
    else:
        template_name = 'auth/login.html'

    res = django_login(request,
                       template_name=template_name,
                       authentication_form=form,
                       extra_context=extra_context)
    # Set the session data here because django's session key rotation
    # will erase it if we set it earlier.
    if request.user.is_authenticated():
        set_session_from_user(request, request.user)
        regions = dict(Login.get_region_choices())
        region = request.user.endpoint
        region_name = regions.get(region)
        request.session['region_endpoint'] = region
        request.session['region_name'] = region_name
    return res

1c)
site-packages/django/contrib/auth/views.py

def login(request, template_name='registration/login.html',
          redirect_field_name=REDIRECT_FIELD_NAME,
          authentication_form=AuthenticationForm,
          current_app=None, extra_context=None):

    ////blabla////
    return TemplateResponse(request, template_name, context,
                            current_app=current_app)


1d)
site-packages/openstack_auth/forms.py

from django.contrib.auth import authenticate

class Login(AuthenticationForm):
    region = forms.ChoiceField(label=_("Region"), required=False)
    username = forms.CharField(label=_("User Name"))
    password = forms.CharField(label=_("Password"),
                               widget=forms.PasswordInput(render_value=False))

    def clean(self):
        default_domain = getattr(settings,
                                 'OPENSTACK_KEYSTONE_DEFAULT_DOMAIN',
                                 'Default')
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')
        region = self.cleaned_data.get('region')
        domain = self.cleaned_data.get('domain', default_domain)

        if not (username and password):
            # Don't authenticate, just let the other validators handle it.
            return self.cleaned_data

        try:
            self.user_cache = authenticate(request=self.request,
                                           username=username,
                                           password=password,
                                           user_domain_name=domain,
                                           auth_url=region)

            msg = 'Login successful for user "%(username)s".' % \
                {'username': username}
            LOG.info(msg)
        except KeystoneAuthException as exc:
            msg = 'Login failed for user "%(username)s".' % \
                {'username': username}
            LOG.warning(msg)
            self.request.session.flush()
            raise forms.ValidationError(exc)
        self.check_for_test_cookie()
        return self.cleaned_data

2)
Same as in django


3)
Same as in django


4)
site-packages/openstack_auth/backend.py

from .user import create_user_from_token
from .user import Token

from .utils import check_token_expiration
from .utils import get_keystone_client

class KeystoneBackend(object):

    def authenticate(self, request=None, username=None, password=None,
                     user_domain_name=None, auth_url=None):

        """Authenticates a user via the Keystone Identity API. """
        LOG.debug('Beginning user authentication for user "%s".' % username)

        insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
        endpoint_type = getattr(
            settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL')

        keystone_client = get_keystone_client()
        try:
            client = keystone_client.Client(
                user_domain_name=user_domain_name,
                username=username,
                password=password,
                auth_url=auth_url,
                insecure=insecure,
                debug=settings.DEBUG)


            unscoped_auth_ref = client.auth_ref
            unscoped_token = Token(auth_ref=unscoped_auth_ref)
        except (keystone_exceptions.Unauthorized,
                keystone_exceptions.Forbidden,
                keystone_exceptions.NotFound) as exc:
            msg = _('Invalid user name or password.')
            LOG.debug(exc.message)
            raise KeystoneAuthException(msg)
        except (keystone_exceptions.ClientException,
                keystone_exceptions.AuthorizationFailure) as exc:
            msg = _("An error occurred authenticating. "
                    "Please try again later.")
            LOG.debug(exc.message)
            raise KeystoneAuthException(msg)

        # Check expiry for our unscoped auth ref.
        self.check_auth_expiry(unscoped_auth_ref)

        # Check if token is automatically scoped to default_project
        if unscoped_auth_ref.project_scoped:
            auth_ref = unscoped_auth_ref
        else:
            # For now we list all the user's projects and iterate through.
            try:
                if get_keystone_version() < 3:
                    projects = client.tenants.list()
                else:
                    client.management_url = auth_url
                    projects = client.projects.list(
                        user=unscoped_auth_ref.user_id)
            except (keystone_exceptions.ClientException,
                    keystone_exceptions.AuthorizationFailure) as exc:
                msg = _('Unable to retrieve authorized projects.')
                raise KeystoneAuthException(msg)

            # Abort if there are no projects for this user
            if not projects:
                msg = _('You are not authorized for any projects.')
                raise KeystoneAuthException(msg)

            while projects:
                project = projects.pop()
                try:
                    client = keystone_client.Client(
                        tenant_id=project.id,
                        token=unscoped_auth_ref.auth_token,
                        auth_url=auth_url,
                        insecure=insecure,
                        debug=settings.DEBUG)
                    auth_ref = client.auth_ref
                    break
                except (keystone_exceptions.ClientException,
                        keystone_exceptions.AuthorizationFailure):
                    auth_ref = None

            if auth_ref is None:
                msg = _("Unable to authenticate to any available projects.")
                raise KeystoneAuthException(msg)

        # Check expiry for our new scoped token.
        self.check_auth_expiry(auth_ref)

        # If we made it here we succeeded. Create our User!
        user = create_user_from_token(
            request,
            Token(auth_ref),
            client.service_catalog.url_for(endpoint_type=endpoint_type))


        if request is not None:
            request.session['unscoped_token'] = unscoped_token.id
            request.user = user

            # Support client caching to save on auth calls.
            setattr(request, KEYSTONE_CLIENT_ATTR, client)

        LOG.debug('Authentication completed for user "%s".' % username)
        return user



3 comments:

  1. How to get region

    site-packages/openstack_auth/forms.py:

    class Login(AuthenticationForm):
    def __init__(self, *args, **kwargs):
    self.fields['region'].choices = self.get_region_choices()
    if len(self.fields['region'].choices) == 1:
    self.fields['region'].initial = self.fields['region'].choices[0][0]
    self.fields['region'].widget = forms.widgets.HiddenInput()

    @staticmethod
    def get_region_choices():
    default_region = (settings.OPENSTACK_KEYSTONE_URL, "Default Region")
    return getattr(settings, 'AVAILABLE_REGIONS', [default_region])

    ReplyDelete
  2. How to test user authentication with openstack_auth and keystoneclient modules
    --------------------------

    ##Activate virtual environment and goto the horizon directory

    ##Goto django console
    python manage.py shell

    ##import django settings
    from django.conf import settings
    dir(settings)
    vars(settings)
    getattr(settings, 'OPENSTACK_API_VERSIONS', {})

    ##Imports
    from openstack_auth import utils as auth_utils
    from openstack_auth.user import Token
    from openstack_auth.utils import check_token_expiration
    dir(auth_utils)

    ##site-packages/openstack_auth/backend.py
    ##client
    auth_utils.get_keystone_version()
    keystone_client = auth_utils.get_keystone_client()
    region_or_auth_url = "http://192.168.56.101:5000/v3"
    ##User authentication on a domain (Default domain) with username and password
    client = keystone_client.Client(
    user_domain_name="Default",
    username="admin",
    password="password",
    auth_url=region_or_auth_url,
    insecure=False,
    debug=True)

    ##Token
    unscoped_auth_ref = client.auth_ref
    unscoped_auth_ref.auth_token
    ##Get token object
    unscoped_token = Token(auth_ref=unscoped_auth_ref)
    check_token_expiration(unscoped_auth_ref)##False means expired

    ##Get all projects, keystone version 3
    client.management_url = region_or_auth_url
    projects = client.projects.list(user=unscoped_auth_ref.user_id)

    ReplyDelete
  3. Create new user
    ------------------------------
    client = keystone_client.Client(
    user_domain_name="Default",
    username="manu",
    password="manu",
    auth_url=region_or_auth_url,
    insecure=False,
    debug=True)

    p = [x for x in client.projects.list() if x.name in ["demo"]]
    u = client.users.create("ss2", password="ss2", project=p[0].id)
    r = [x for x in client.roles.list() if x.name in ["Member"]]
    client.roles.grant(r[0], user=u.id, project=p[0].id)

    ReplyDelete