[Checkins]
SVN: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/
ongoing spike: integration of PlainLoginDemo code
Luciano Ramalho
luciano at ramalho.org
Mon Feb 18 21:13:39 EST 2008
Log message for revision 84042:
ongoing spike: integration of PlainLoginDemo code
Changed:
U Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/__init__.py
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/configure.zcml
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/directives.py
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/form.pt
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views.py
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/index.pt
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/listing.pt
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/login.pt
A Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/master.pt
-=-
Modified: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/__init__.py
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/__init__.py 2008-02-19 00:34:52 UTC (rev 84041)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/__init__.py 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,2 @@
+from megrok.simpleauth.directives import setup_authentication_if_it_pleases_you_mr_grok
+import views
\ No newline at end of file
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/configure.zcml
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/configure.zcml (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/configure.zcml 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,5 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:grok="http://namespaces.zope.org/grok">
+ <include package="megrok.simpleauth" />
+ <grok:grok package="." />
+</configure>
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/directives.py
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/directives.py (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/directives.py 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,34 @@
+from zope.app.authentication import PluggableAuthentication
+from zope.app.security.interfaces import IAuthentication
+from zope.app.authentication.principalfolder import PrincipalFolder
+from zope.app.authentication.session import SessionCredentialsPlugin
+
+from grok.directive import MultipleTimesDirective, ClassDirectiveContext, LocalUtilityInfo
+
+def setup_pau(pau):
+ '''
+ Callback to setup the Pluggable Authentication Utility
+
+ A reference to this function is passed as a parameter in the
+ declaration of the PAU (see PlainLoginDemo class)
+ '''
+ # the principal source is a PrincipalFolder, stored in ZODB
+ pau['principals'] = PrincipalFolder()
+ pau.authenticatorPlugins = ('principals',)
+ # the SessionCredentialsPlugin isused for cookie-based authentication
+ pau['session'] = session = SessionCredentialsPlugin()
+ session.loginpagename = 'login' # the page to redirect for login
+ # configuration of the credentials plugin
+ pau.credentialsPlugins = ('No Challenge if Authenticated', 'session',)
+
+class SetupAuthenticationDirective(MultipleTimesDirective):
+ def check_arguments(self):
+ pass
+
+ def value_factory(self):
+
+ return LocalUtilityInfo(PluggableAuthentication, provides=IAuthentication, name=u'',
+ setup=setup_pau, public=False, name_in_container=None)
+
+setup_authentication_if_it_pleases_you_mr_grok = SetupAuthenticationDirective('grok.local_utility',
+ ClassDirectiveContext())
\ No newline at end of file
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/form.pt
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/form.pt (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/form.pt 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,75 @@
+<html metal:use-macro="context/@@master/macros/page">
+<body>
+
+ <div metal:fill-slot="main">
+
+<!-- the code of the form element below was copied verbatim from
+ src/grok/templates/default_edit_form.pt-->
+<!-- XXX: is a better way of rendering Groks's default forms inside
+ a master template?-->
+
+<form action="." tal:attributes="action request/URL" method="post"
+ class="edit-form" enctype="multipart/form-data">
+
+ <h1 i18n:translate=""
+ tal:condition="view/label"
+ tal:content="view/label">Label</h1>
+
+ <div class="form-status"
+ tal:define="status view/status"
+ tal:condition="status">
+
+ <div i18n:translate="" tal:content="view/status">
+ Form status summary
+ </div>
+
+ <ul class="errors" tal:condition="view/errors">
+ <li tal:repeat="error view/error_views">
+ <span tal:replace="structure error">Error Type</span>
+ </li>
+ </ul>
+ </div>
+
+ <table class="form-fields">
+ <tbody>
+ <tal:block repeat="widget view/widgets">
+ <tr>
+ <td class="label" tal:define="hint widget/hint">
+ <label tal:condition="python:hint"
+ tal:attributes="for widget/name">
+ <span class="required" tal:condition="widget/required"
+ >*</span><span i18n:translate=""
+ tal:content="widget/label">label</span>
+ </label>
+ <label tal:condition="python:not hint"
+ tal:attributes="for widget/name">
+ <span class="required" tal:condition="widget/required"
+ >*</span><span i18n:translate=""
+ tal:content="widget/label">label</span>
+ </label>
+ </td>
+ <td class="field">
+ <div class="widget" tal:content="structure widget">
+ <input type="text" />
+ </div>
+ <div class="error" tal:condition="widget/error">
+ <span tal:replace="structure widget/error">error</span>
+ </div>
+ </td>
+ </tr>
+ </tal:block>
+ </tbody>
+ </table>
+
+ <div id="actionsView">
+ <span class="actionButtons" tal:condition="view/availableActions">
+ <input tal:repeat="action view/actions"
+ tal:replace="structure action/render"
+ />
+ </span>
+ </div>
+</form>
+
+ </div><!--/slot main-->
+</body>
+</html>
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views.py
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views.py (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views.py 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,179 @@
+import grok
+
+from urllib import urlencode
+
+from zope.interface import Interface, implements
+from zope.component import getUtility, getUtilitiesFor
+from zope.app.authentication import PluggableAuthentication
+from zope.app.authentication.interfaces import IPasswordManager
+from zope.app.authentication.principalfolder import PrincipalFolder
+from zope.app.authentication.principalfolder import InternalPrincipal
+from zope.app.authentication.principalfolder import IInternalPrincipal
+from zope.app.authentication.session import SessionCredentialsPlugin
+from zope.app.container.interfaces import DuplicateIDError
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
+from zope.app.form.browser import RadioWidget, TextWidget
+from zope.security.management import checkPermission
+from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager
+from zope.schema import getFieldNamesInOrder, ValidationError
+from zope.schema.interfaces import IField, IIterableSource
+from zope.i18n import MessageFactory
+
+_ = MessageFactory('megrok.simpleauth')
+
+class ViewMemberListing(grok.Permission):
+ ''' Permission to see the member listing '''
+ grok.name('plainlogindemo.ViewMemberListing')
+
+class Master(grok.View):
+ """
+ The master page template macro.
+
+ The template master.pt is used as page macro in most views. Since this
+ template uses the logged_in method and message attributes below, it's best
+ to make all other views in this app subclasses of Master.
+ """
+ grok.context(Interface) # register this view for all objects
+
+ message = '' # used to give feedback
+
+ def logged_in(self):
+ # this is the canonical way to tell whether the user is authenticated
+ # in Zope 3: check if the principal provides IUnauthenticatedPrincipal
+ return not IUnauthenticatedPrincipal.providedBy(self.request.principal)
+
+class Index(Master):
+ """
+ The main page, showing user data and member count.
+ """
+
+ def member_count(self):
+ # get the authentication utility
+ pau = getUtility(IAuthentication)
+ return len(pau['principals'])
+
+
+class Login(Master):
+ """
+ Login form and handler.
+ """
+ def update(self, login_submit=None):
+ if login_submit is not None: # we are handling the login submission
+ if self.logged_in(): # if the login was accepted then...
+ # redirect to where the user came from, or to the main page
+ dest = self.request.get('camefrom', self.application_url())
+ self.redirect(dest)
+ else: # if the user is still not logged in...
+ # then an incorrect login or password was provided
+ self.message = _(u'Invalid login name and/or password')
+
+class Logout(grok.View):
+ """
+ Logout handler.
+ """
+ grok.context(Interface)
+ def render(self):
+ # get the session plugin and tell it to logout
+ session = getUtility(IAuthentication)['session']
+ session.logout(self.request)
+ # redirect to the main page
+ self.redirect(self.application_url())
+
+class FullNameWidget(TextWidget):
+ """
+ Simple customization: change field label from 'Title' to 'Full Name',
+ which makes more sense for a user record.
+ """
+
+ label = _(u'Full Name')
+
+class PasswordManagerChoices(RadioWidget):
+ """
+ Widget to select the passwordManager in charge of hashing or encrypting
+ the user's password before storing it.
+ """
+
+ label = _(u'Password protection')
+
+ def __init__(self, field, request):
+ # the IInternalPrincipal.passwordManagerName field comes with a
+ # vocabulary which provides the available utilities providing
+ # IPasswordManager; here we just pass that vocabulary to the widget
+ super(PasswordManagerChoices, self).__init__(
+ field, field.vocabulary, request)
+
+class Join(grok.AddForm, Master):
+ """
+ User registration form.
+ """
+
+ form_fields = grok.AutoFields(IInternalPrincipal)
+ # use our customized widgets
+ form_fields['title'].custom_widget = FullNameWidget
+ form_fields['passwordManagerName'].custom_widget = PasswordManagerChoices
+
+ label = _(u'User registration')
+ template = grok.PageTemplateFile('form.pt')
+
+ @grok.action('Save')
+ def save(self, **data):
+ '''
+ Create an InternalPrincipal with the user data.
+
+ This method also grants the ViewMemberListing permission to the user.
+ '''
+ login = data['login']
+ pau = getUtility(IAuthentication)
+ principals = pau['principals']
+ # create an instance of InternalPrincipal
+ principal = InternalPrincipal(**data)
+ try:
+ principals[login] = principal
+ except DuplicateIDError:
+ # create a validation exception and assign it to the login field
+ msg = _(u'Login name taken. Please choose a different one.')
+ self.widgets['login']._error = ValidationError(msg)
+ self.form_reset = False # preserve the values in the fields
+ else:
+ # grant the user permission to view the member listing
+ permission_mngr = IPrincipalPermissionManager(grok.getSite())
+ permission_mngr.grantPermissionToPrincipal(
+ 'plainlogindemo.ViewMemberListing', principals.prefix + login)
+ self.redirect(self.url('login')+'?'+urlencode({'login':login}))
+
+class Account(grok.View):
+
+ def render(self):
+ return 'Not implemented'
+
+class Listing(Master):
+ '''
+ Member listing view.
+
+ This demonstrates how to require a permission to view, and also how to
+ obtain a list of annotated principals.
+ '''
+
+ grok.require('plainlogindemo.ViewMemberListing')
+
+ def field_names(self):
+ return getFieldNamesInOrder(IInternalPrincipal)
+
+ def members(self):
+ pau = getUtility(IAuthentication)
+ principals = pau['principals']
+ roster = []
+ for id in sorted(principals.keys()):
+ user = principals[id]
+ fields = {}
+ for field in self.field_names():
+ fields[field] = getattr(user, field)
+ roster.append(fields)
+ return roster
+
+ def delete_allowed(self):
+ # XXX: this is not the right way to do it... it's just a test
+ return self.request.principal.id.endswith('.manager')
+
+
\ No newline at end of file
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/index.pt
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/index.pt (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/index.pt 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,42 @@
+<html metal:use-macro="context/@@master/macros/page">
+<body>
+<div metal:fill-slot="main">
+ <h1>Login Demo Main Page</h1>
+
+ <table><tr>
+ <td valign="top">
+ <h2>User information</h2>
+ <dl>
+ <dt>principal.id</dt>
+ <dd tal:content="request/principal/id" />
+
+ </dl>
+ <dl>
+ <dt>principal.title</dt>
+ <dd tal:content="request/principal/title" />
+
+ </dl>
+
+ <p>
+ You are <em tal:condition="not:view/logged_in">not </em>logged in.
+ </p>
+
+ <tal:not_logged_in condition="not:view/logged_in">
+
+ <form metal:use-macro="context/@@login/macros/loginform" />
+
+ </tal:not_logged_in>
+ </td>
+ <td width="30%"></td>
+ <td valign="top">
+ <h2>Membership</h2>
+
+ <h3>Member accounts: <span tal:replace="view/member_count" /></h3>
+
+ <p><a href="listing">View member listing</a> (requires login)</p>
+
+ </td>
+ </tr></table>
+</div>
+</body>
+</html>
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/listing.pt
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/listing.pt (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/listing.pt 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,22 @@
+<html metal:use-macro="context/@@master/macros/page">
+<body>
+<div metal:fill-slot="main">
+ <h1>Member Listing</h1>
+
+ <table>
+ <tr><th tal:repeat="field view/field_names"
+ tal:content="field">Field name</th></tr>
+ <tr valign="top" tal:repeat="member view/members">
+ <td bgcolor="lightgray"
+ tal:repeat="field view/field_names"
+ tal:content="python:member[field]"></td>
+ <td tal:condition="view/delete_allowed">
+ <form tal:attributes="action view/url" method="post">
+ <input type="submit" value="Delete" />
+ </form>
+ </td>
+ </tr>
+ </table>
+</div>
+</body>
+</html>
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/login.pt
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/login.pt (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/login.pt 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,36 @@
+<html metal:use-macro="context/@@master/macros/page">
+<body>
+<div metal:fill-slot="main">
+ <h1>Login</h1>
+
+ <form metal:define-macro="loginform" action="login" method="post">
+ <input type="hidden" name="camefrom"
+ tal:condition="exists:request/camefrom"
+ tal:attributes="value request/camefrom" />
+
+ <table>
+ <tr>
+ <th>Login</th>
+ <td>
+ <input type="text" name="login" id="login"
+ tal:attributes="value request/login|nothing"/>
+ </td>
+ </tr>
+ <tr>
+ <th>Password</th>
+ <td>
+ <input type="password" name="password" id="password" />
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>
+ <input type="submit" name="login_submit" value="Log in" />
+ </td>
+ </tr>
+ </table>
+ </form>
+
+</div>
+</body>
+</html>
Added: Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/master.pt
===================================================================
--- Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/master.pt (rev 0)
+++ Sandbox/lra/megrok.simpleauth/src/megrok/simpleauth/views_templates/master.pt 2008-02-19 02:13:37 UTC (rev 84042)
@@ -0,0 +1,34 @@
+<html metal:define-macro="page">
+<head>
+<title>Grok Login Sample Application</title>
+</head>
+<body>
+ <table bgcolor="lightgray" width="100%">
+ <tr tal:condition="not:view/logged_in">
+ <td width="10%"><a href="index">main</a></td>
+ <td width="70%">you are not logged in</td>
+ <td width="10%"><a href="join">join</a></td>
+ <td width="10%"><a href="login">login</a></td>
+ </tr>
+ <tr tal:condition="view/logged_in">
+ <td width="10%"><a href="index">main</a></td>
+ <td width="70%">logged in as
+ <span tal:replace="string:${request/principal/title}
+ (${request/principal/id})">
+ principal.title (principal.id)
+ </span>
+ </td>
+ <td width="10%"><a href="account">account</a></td>
+ <td width="10%"><a href="logout">logout</a></td>
+ </tr>
+ </table>
+ <div style="background: yellow;"
+ tal:condition="view/message"
+ tal:content="view/message"
+ />
+ <div metal:define-slot="main">
+ Here goes the main content of the page
+ </div>
+
+</body>
+</html>
More information about the Checkins
mailing list