This repository has been archived by the owner on Feb 17, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Fix for issue 682: Quoting realm in WWW-Authenticate header properly
- Loading branch information
Christian Theune
committed
Aug 14, 2006
1 parent
6751fca
commit e73a775
Showing
5 changed files
with
605 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
############################################################################## | ||
# | ||
# Copyright (c) 2001, 2002 Zope Corporation and Contributors. | ||
# 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. | ||
# | ||
############################################################################## | ||
"""HTTP Basic Authentication adapter | ||
$Id$ | ||
""" | ||
from zope.publisher.interfaces.http import IHTTPCredentials | ||
from loginpassword import LoginPassword | ||
|
||
|
||
class BasicAuthAdapter(LoginPassword): | ||
"""Adapter for handling HTTP Basic Auth.""" | ||
|
||
__used_for__ = IHTTPCredentials | ||
|
||
__request = None | ||
|
||
def __init__(self, request): | ||
self.__request = request | ||
# TODO base64 decoding should be done here, not in request | ||
lpw = request._authUserPW() | ||
if lpw is None: | ||
login, password = None, None | ||
else: | ||
login, password = lpw | ||
LoginPassword.__init__(self, login, password) | ||
|
||
def needLogin(self, realm): | ||
self.__request.unauthorized('basic realm="%s"'% realm) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
############################################################################## | ||
# | ||
# Copyright (c) 2003 Zope Corporation and Contributors. | ||
# 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. | ||
# | ||
############################################################################## | ||
"""Login and Logout screens | ||
$Id$ | ||
""" | ||
import urllib | ||
from zope.interface import implements | ||
from zope.i18n import translate | ||
from zope import component | ||
from zope.app.publisher.interfaces.http import ILogin | ||
from zope.app.security.interfaces import IAuthentication | ||
from zope.app.security.interfaces import IUnauthenticatedPrincipal | ||
from zope.app.security.interfaces import ILogout, ILogoutSupported | ||
from zope.app.pagetemplate import ViewPageTemplateFile | ||
from zope.app.i18n import ZopeMessageFactory as _ | ||
|
||
|
||
search_label = _('search-button', 'Search') | ||
search_title = _('Search String') | ||
|
||
class AuthUtilitySearchView(object): | ||
|
||
__used_for__ = IAuthentication | ||
|
||
def __init__(self, context, request): | ||
self.context = context | ||
self.request = request | ||
|
||
def render(self, name): | ||
sourcename = 'principals.zcml' | ||
html = [] | ||
|
||
# add sub title for source search field | ||
html.append('<h4>%s</h4>' % sourcename) | ||
# start row for search fields | ||
html.append('<div class="row">') | ||
html.append('<div class="label">') | ||
html.append(translate(search_title, context=self.request)) | ||
html.append('</div>') | ||
html.append('<div class="field">') | ||
html.append('<input type="text" name="%s" />' %(name+'.searchstring')) | ||
html.append('</div>') | ||
html.append('</div>') | ||
|
||
# add search button for search fields | ||
html.append('<div class="row">') | ||
html.append('<div class="field">') | ||
html.append('<input type="submit" name="%s" value="%s" />' | ||
% (name+'.search', | ||
translate(search_label, context=self.request))) | ||
html.append('</div>') | ||
|
||
# end row | ||
html.append('</div>') | ||
|
||
return '\n'.join(html) | ||
|
||
def results(self, name): | ||
if not (name+'.search' in self.request): | ||
return None | ||
searchstring = self.request[name+'.searchstring'] | ||
return [principal.id | ||
for principal in self.context.getPrincipals(searchstring)] | ||
|
||
|
||
class HTTPAuthenticationLogin(object): | ||
|
||
implements(ILogin) | ||
|
||
confirmation = ViewPageTemplateFile('login.pt') | ||
|
||
failed = ViewPageTemplateFile('login_failed.pt') | ||
|
||
def login(self, nextURL=None): | ||
# we don't want to keep challenging if we're authenticated | ||
if IUnauthenticatedPrincipal.providedBy(self.request.principal): | ||
component.getUtility(IAuthentication).unauthorized( | ||
self.request.principal.id, self.request) | ||
return self.failed() | ||
else: | ||
if nextURL is None: | ||
return self.confirmation() | ||
else: | ||
self.request.response.redirect(nextURL) | ||
|
||
|
||
class HTTPBasicAuthenticationLogin(HTTPAuthenticationLogin): | ||
"""Issues a challenge to the browser to get basic auth credentials. | ||
This view can be used as a fail safe login in the even the normal login | ||
fails because of an improperly configured authentication utility. | ||
The failsafeness of this view relies on the fact that the global principal | ||
registry, which typically contains an adminitrator principal, uses basic | ||
auth credentials to authenticate. | ||
""" | ||
def login(self, nextURL=None): | ||
# we don't want to keep challenging if we're authenticated | ||
if IUnauthenticatedPrincipal.providedBy(self.request.principal): | ||
# hard-code basic auth challenge | ||
self.request.unauthorized('basic realm="Zope"') | ||
return self.failed() | ||
else: | ||
if nextURL is None: | ||
return self.confirmation() | ||
else: | ||
self.request.response.redirect(nextURL) | ||
|
||
|
||
class HTTPAuthenticationLogout(object): | ||
"""Since HTTP Authentication really does not know about logout, we are | ||
simply challenging the client again.""" | ||
|
||
implements(ILogout) | ||
|
||
confirmation = ViewPageTemplateFile('logout.pt') | ||
|
||
redirect = ViewPageTemplateFile('redirect.pt') | ||
|
||
def __init__(self, context, request): | ||
self.context = context | ||
self.request = request | ||
|
||
def logout(self, nextURL=None): | ||
if not IUnauthenticatedPrincipal.providedBy(self.request.principal): | ||
auth = component.getUtility(IAuthentication) | ||
ILogout(auth).logout(self.request) | ||
if nextURL: | ||
return self.redirect() | ||
if nextURL is None: | ||
return self.confirmation() | ||
else: | ||
return self.request.response.redirect(nextURL) | ||
|
||
|
||
class LoginLogout(object): | ||
|
||
def __init__(self, context, request): | ||
self.context = context | ||
self.request = request | ||
|
||
def __call__(self): | ||
if IUnauthenticatedPrincipal.providedBy(self.request.principal): | ||
return u'<a href="@@login.html?nextURL=%s">%s</a>' % ( | ||
urllib.quote(self.request.getURL()), | ||
translate(_('[Login]'), context=self.request, | ||
default='[Login]')) | ||
elif ILogoutSupported(self.request, None) is not None: | ||
return u'<a href="@@logout.html?nextURL=%s">%s</a>' % ( | ||
urllib.quote(self.request.getURL()), | ||
translate(_('[Logout]'), context=self.request, | ||
default='[Logout]')) | ||
else: | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
############################################################################## | ||
# | ||
# Copyright (c) 2001, 2002 Zope Corporation and Contributors. | ||
# 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. | ||
# | ||
############################################################################## | ||
"""Global Authentication Utility or Principal Registry | ||
$Id$ | ||
""" | ||
from zope.interface import implements | ||
|
||
from zope.app.authentication.interfaces import IPasswordManager | ||
from zope.app.security.interfaces import PrincipalLookupError | ||
from zope.app import zapi | ||
from zope.security.interfaces import IPrincipal, IGroupAwarePrincipal | ||
from zope.app.security import interfaces | ||
from zope.app.container.contained import Contained, contained | ||
|
||
|
||
class DuplicateLogin(Exception): pass | ||
class DuplicateId(Exception): pass | ||
|
||
class PrincipalRegistry(object): | ||
|
||
implements(interfaces.IAuthentication, interfaces.ILogout) | ||
|
||
# Methods implementing IAuthentication | ||
|
||
def authenticate(self, request): | ||
a = interfaces.ILoginPassword(request, None) | ||
if a is not None: | ||
login = a.getLogin() | ||
if login is not None: | ||
p = self.__principalsByLogin.get(login, None) | ||
if p is not None: | ||
password = a.getPassword() | ||
if p.validate(password): | ||
return p | ||
return None | ||
|
||
__defaultid = None | ||
__defaultObject = None | ||
|
||
def defineDefaultPrincipal(self, id, title, description='', | ||
principal=None): | ||
if id in self.__principalsById: | ||
raise DuplicateId(id) | ||
self.__defaultid = id | ||
if principal is None: | ||
principal = UnauthenticatedPrincipal(id, title, description) | ||
self.__defaultObject = contained(principal, self, id) | ||
return principal | ||
|
||
def unauthenticatedPrincipal(self): | ||
return self.__defaultObject | ||
|
||
def unauthorized(self, id, request): | ||
if id is None or id is self.__defaultid: | ||
a = interfaces.ILoginPassword(request) | ||
a.needLogin(realm="Zope") | ||
|
||
def getPrincipal(self, id): | ||
r = self.__principalsById.get(id) | ||
if r is None: | ||
if id == self.__defaultid: | ||
return self.__defaultObject | ||
raise PrincipalLookupError(id) | ||
return r | ||
|
||
def getPrincipalByLogin(self, login): | ||
return self.__principalsByLogin[login] | ||
|
||
def getPrincipals(self, name): | ||
name = name.lower() | ||
return [p for p in self.__principalsById.itervalues() | ||
if p.title.lower().startswith(name) or | ||
p.getLogin().lower().startswith(name)] | ||
|
||
def logout(self, request): | ||
# not supporting basic auth logout -- no such thing | ||
pass | ||
|
||
# Management methods | ||
|
||
def __init__(self): | ||
self.__principalsById = {} | ||
self.__principalsByLogin = {} | ||
|
||
def definePrincipal(self, principal, title, description='', | ||
login='', password='', passwordManagerName='Plain Text'): | ||
id=principal | ||
if login in self.__principalsByLogin: | ||
raise DuplicateLogin(login) | ||
|
||
if id in self.__principalsById or id == self.__defaultid: | ||
raise DuplicateId(id) | ||
|
||
p = Principal(id, title, description, | ||
login, password, passwordManagerName) | ||
p = contained(p, self, id) | ||
|
||
self.__principalsByLogin[login] = p | ||
self.__principalsById[id] = p | ||
|
||
return p | ||
|
||
def registerGroup(self, group): | ||
id = group.id | ||
if id in self.__principalsById or id == self.__defaultid: | ||
raise DuplicateId(id) | ||
|
||
self.__principalsById[group.id] = group | ||
|
||
def _clear(self): | ||
self.__init__() | ||
self.__defaultid = None | ||
self.__defaultObject = None | ||
|
||
principalRegistry = PrincipalRegistry() | ||
|
||
# Register our cleanup with Testing.CleanUp to make writing unit tests simpler. | ||
from zope.testing.cleanup import addCleanUp | ||
addCleanUp(principalRegistry._clear) | ||
del addCleanUp | ||
|
||
class PrincipalBase(Contained): | ||
|
||
def __init__(self, id, title, description): | ||
self.id = id | ||
self.title = title | ||
self.description = description | ||
self.groups = [] | ||
|
||
class Group(PrincipalBase): | ||
|
||
def getLogin(self): | ||
return '' # to make registry search happy | ||
|
||
class Principal(PrincipalBase): | ||
|
||
implements(IGroupAwarePrincipal) | ||
|
||
def __init__(self, id, title, description, login, | ||
pw, pwManagerName="Plain Text"): | ||
super(Principal, self).__init__(id, title, description) | ||
self.__login = login | ||
self.__pwManagerName = pwManagerName | ||
self.__pw = pw | ||
|
||
def __getPasswordManager(self): | ||
return zapi.getUtility(IPasswordManager, self.__pwManagerName) | ||
|
||
def getLogin(self): | ||
return self.__login | ||
|
||
def validate(self, pw): | ||
pwManager = self.__getPasswordManager() | ||
return pwManager.checkPassword(self.__pw, pw) | ||
|
||
|
||
class UnauthenticatedPrincipal(PrincipalBase): | ||
|
||
implements(interfaces.IUnauthenticatedPrincipal) | ||
|
||
class UnauthenticatedGroup(Group): | ||
|
||
implements(interfaces.IUnauthenticatedGroup) | ||
|
||
class AuthenticatedGroup(Group): | ||
|
||
implements(interfaces.IAuthenticatedGroup) | ||
|
||
class EverybodyGroup(Group): | ||
|
||
implements(interfaces.IEveryoneGroup) | ||
|
Oops, something went wrong.