--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_security/views/login.py Thu Feb 19 10:53:29 2015 +0100
@@ -0,0 +1,201 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_security.interfaces import ILoginView, ISecurityManager, LOGIN_REFERER_KEY
+from pyams_skin.interfaces import IModalFullPage
+from pyams_skin.layer import IPyAMSLayer
+from z3c.form.interfaces import IDataExtractedEvent
+
+# import packages
+from pyams_form.form import AddForm, AJAXAddForm, DialogAddForm
+from pyams_form.schema import ResetButton, CloseButton
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_security.credential import Credentials
+from pyams_utils.registry import query_utility
+from pyramid.events import subscriber
+from pyramid.httpexceptions import HTTPFound
+from pyramid.response import Response
+from pyramid.security import remember, forget
+from pyramid.view import view_config, forbidden_view_config
+from z3c.form import field, button
+from zope.interface import implementer, Interface, Invalid
+from zope.schema import TextLine, Password
+
+from pyams_security import _
+
+
+#
+# Login views
+#
+
+@forbidden_view_config(request_type=IPyAMSLayer)
+def ForbiddenView(request):
+ """Default forbidden view"""
+ request.session[LOGIN_REFERER_KEY] = request.view_name
+ return HTTPFound('login.html')
+
+
+class ILoginFormFields(Interface):
+ """Login form fields"""
+
+ login = TextLine(title=_("Login"))
+ password = Password(title=_("Password"))
+
+
+class ILoginFormButtons(Interface):
+ """Login form buttons"""
+
+ reset = ResetButton(name='reset', title=_("Reset"))
+ login = button.Button(name='login', title=_("Connect"))
+
+
+@subscriber(IDataExtractedEvent, form_selector=ILoginView)
+def handle_login_form_data(event):
+ """Check credentials after data extraction"""
+ data = event.data
+ if 'principal_id' in data:
+ del data['principal_id']
+ manager = query_utility(ISecurityManager)
+ if manager is not None:
+ credentials = Credentials('form', id=data['login'], **data)
+ principal_id = manager.authenticate(credentials, event.form.request)
+ if principal_id is None:
+ event.form.widgets.errors += (Invalid(_("Invalid credentials!")),)
+ else:
+ data['principal_id'] = principal_id
+ else:
+ event.form.widgets.errors += (Invalid(_("Missing security manager utility. Please contact administrator!")),)
+
+
+@pagelet_config(name='login.html', layer=IPyAMSLayer)
+@implementer(IModalFullPage, ILoginView)
+class LoginForm(AddForm):
+ """Login form"""
+
+ legend = _("Please enter valid credentials to log-in")
+ fields = field.Fields(ILoginFormFields)
+ buttons = button.Buttons(ILoginFormButtons)
+ ajax_handler = 'login.json'
+ edit_permission = None
+
+ def updateActions(self):
+ super(LoginForm, self).updateActions()
+ if 'login' in self.actions:
+ self.actions['login'].addClass('btn-primary')
+
+ def createAndAdd(self, data):
+ principal_id = data.get('principal_id')
+ if principal_id is not None:
+ headers = remember(self.request, principal_id)
+ response = self.request.response
+ response.headerlist.extend(headers)
+ if not self.request.is_xhr:
+ response.status_code = 302
+ session = self.request.session
+ if LOGIN_REFERER_KEY in session:
+ response.location = session[LOGIN_REFERER_KEY]
+ del session[LOGIN_REFERER_KEY]
+ else:
+ response.location = '/'
+
+
+@view_config(name='login.json', request_type=IPyAMSLayer, renderer='json', xhr=True)
+class LoginAJAXForm(AJAXAddForm, LoginForm):
+ """Login form, AJAX view"""
+
+ def get_ajax_output(self, changes):
+ status = {'status': 'redirect'}
+ session = self.request.session
+ if LOGIN_REFERER_KEY in session:
+ status['location'] = session[LOGIN_REFERER_KEY]
+ del session[LOGIN_REFERER_KEY]
+ return status
+
+
+@forbidden_view_config(request_type=IPyAMSLayer, renderer='json', xhr=True)
+def ForbiddenAJAXView(request):
+ """AJAX call forbidden view"""
+ return {'status': 'modal',
+ 'location': 'login-dialog.html'}
+
+
+class ILoginDialogFormButtons(Interface):
+ """Login dialog form buttons"""
+
+ close = CloseButton(name='close', title=_("Cancel"))
+ login = button.Button(name='login', title=_("Connect"))
+
+
+@pagelet_config(name='login-dialog.html', layer=IPyAMSLayer)
+@implementer(ILoginView)
+class LoginDialogForm(DialogAddForm):
+ """Login dialog form"""
+
+ title = _("Please enter valid credentials to log-in")
+ legend = None
+ fields = field.Fields(ILoginFormFields)
+ buttons = button.Buttons(ILoginDialogFormButtons)
+ ajax_handler = 'login-dialog.json'
+ edit_permission = None
+
+ def update(self):
+ super(LoginDialogForm, self).update()
+ self.request.session[LOGIN_REFERER_KEY] = self.request.referer
+
+ def updateActions(self):
+ super(LoginDialogForm, self).updateActions()
+ if 'login' in self.actions:
+ self.actions['login'].addClass('btn-primary')
+
+ def createAndAdd(self, data):
+ credentials = Credentials('form', id=data['login'], **data)
+ manager = query_utility(ISecurityManager)
+ if manager is not None:
+ principal_id = manager.authenticate(credentials, self.request)
+ if principal_id is not None:
+ headers = remember(self.request, principal_id)
+ response = self.request.response
+ response.headerlist.extend(headers)
+
+
+@view_config(name='login-dialog.json', request_type=IPyAMSLayer, renderer='json', xhr=True)
+class LoginDialogAJAXForm(AJAXAddForm, LoginDialogForm):
+ """Login dialog form, AJAX view"""
+
+ def get_ajax_output(self, changes):
+ status = {'status': 'redirect'}
+ session = self.request.session
+ if LOGIN_REFERER_KEY in session:
+ status['location'] = session[LOGIN_REFERER_KEY]
+ del session[LOGIN_REFERER_KEY]
+ return status
+
+
+#
+# Logout view
+#
+
+@view_config(name='logout.html', request_type=IPyAMSLayer)
+def logout(request):
+ """Logout view"""
+ headers = forget(request)
+ response = Response()
+ response.headerlist.extend(headers)
+ response.status_code = 302
+ response.location = request.referer or '/'
+ return response