Skip to content

Commit

Permalink
Centralize basic auth header de/encoding.
Browse files Browse the repository at this point in the history
  • Loading branch information
hannosch committed May 14, 2017
1 parent d72b134 commit d007655
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 23 deletions.
6 changes: 4 additions & 2 deletions src/OFS/tests/test_userfolder.py
Expand Up @@ -12,8 +12,11 @@
##############################################################################
""" Unit tests for OFS.userfolder
"""

import unittest

from ZPublisher.utils import basic_auth_encode

# TODO class Test_readUserAccessFile(unittest.TestCase)


Expand Down Expand Up @@ -68,8 +71,7 @@ def _makeApp(self):
return app

def _makeBasicAuthToken(self, creds='user1:secret'):
import base64
return 'Basic %s' % base64.encodestring(creds)
return basic_auth_encode(creds)

def _login(self, uf, name):
from AccessControl.SecurityManagement import newSecurityManager
Expand Down
4 changes: 2 additions & 2 deletions src/Testing/ZopeTestCase/functional.py
Expand Up @@ -15,7 +15,6 @@
After Marius Gedminas' functional.py module for Zope3.
"""

import base64
from functools import partial
import sys

Expand All @@ -26,6 +25,7 @@
from Testing.ZopeTestCase import interfaces
from Testing.ZopeTestCase import sandbox
from ZPublisher.httpexceptions import HTTPExceptionHandler
from ZPublisher.utils import basic_auth_encode


def savestate(func):
Expand Down Expand Up @@ -90,7 +90,7 @@ def publish(self, path, basic=None, env=None, extra=None,
raise TypeError('')

if basic:
env['HTTP_AUTHORIZATION'] = "Basic %s" % base64.encodestring(basic)
env['HTTP_AUTHORIZATION'] = basic_auth_encode(basic)

if stdin is None:
stdin = BytesIO()
Expand Down
9 changes: 4 additions & 5 deletions src/Testing/ZopeTestCase/zopedoctest/functional.py
Expand Up @@ -13,7 +13,6 @@
"""Support for (functional) doc tests
"""

import base64
import doctest
from functools import partial
import re
Expand All @@ -34,6 +33,8 @@
from Testing.ZopeTestCase.functional import ResponseWrapper
from Testing.ZopeTestCase.functional import savestate
from ZPublisher.httpexceptions import HTTPExceptionHandler
from ZPublisher.utils import basic_auth_encode


if sys.version_info >= (3, ):
basestring = str
Expand Down Expand Up @@ -106,8 +107,7 @@ def auth_header(header):
u = ''
if p is None:
p = ''
auth = base64.encodestring('%s:%s' % (u, p))
return 'Basic %s' % auth[:-1]
return basic_auth_encode(u, p)
return header


Expand Down Expand Up @@ -307,8 +307,7 @@ def setup_globs(self):
globs['http'] = http
globs['getRootFolder'] = getRootFolder
globs['sync'] = sync
unencoded_user_auth = ('%s:%s' % (user_name, user_password)).encode('utf-8')
globs['user_auth'] = base64.encodestring(unencoded_user_auth)
globs['user_auth'] = basic_auth_encode(user_name, user_password)

def setup_test_class(self):
test_class = self._kw.get('test_class', FunctionalTestCase)
Expand Down
11 changes: 4 additions & 7 deletions src/ZPublisher/HTTPRequest.py
Expand Up @@ -18,6 +18,7 @@
from cgi import escape
from cgi import FieldStorage
import codecs
import collections
from copy import deepcopy
import os
from os import unlink
Expand Down Expand Up @@ -45,7 +46,7 @@
from ZPublisher.BaseRequest import BaseRequest
from ZPublisher.BaseRequest import quote
from ZPublisher.Converters import get_converter
import collections
from ZPublisher.utils import basic_auth_decode

if sys.version_info >= (3, ):
unicode = str
Expand Down Expand Up @@ -1534,12 +1535,8 @@ def text(self):
return result

def _authUserPW(self):
auth = self._auth
if auth:
if auth[:6].lower() == 'basic ':
[name, password] = \
base64.decodestring(auth.split()[-1]).split(':', 1)
return name, password
# Can return None
return basic_auth_decode(self._auth)

def taintWrapper(self, enabled=TAINTING_ENABLED):
return enabled and TaintRequestWrapper(self) or self
Expand Down
21 changes: 16 additions & 5 deletions src/ZPublisher/tests/testHTTPRequest.py
@@ -1,4 +1,16 @@
import base64
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# 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.
#
##############################################################################

from io import BytesIO
import sys
import unittest
Expand All @@ -12,6 +24,7 @@
from zope.testing.cleanup import cleanUp

from ZPublisher.tests.testBaseRequest import TestRequestViewsBase
from ZPublisher.utils import basic_auth_encode

if sys.version_info >= (3, ):
unicode = str
Expand Down Expand Up @@ -755,8 +768,7 @@ def test_processInputs_with_file_upload_gets_iterator(self):
def test__authUserPW_simple(self):
user_id = 'user'
password = 'password'
encoded = base64.encodestring('%s:%s' % (user_id, password))
auth_header = 'basic %s' % encoded
auth_header = basic_auth_encode(user_id, password)

environ = {'HTTP_AUTHORIZATION': auth_header}
request = self._makeOne(environ=environ)
Expand All @@ -770,8 +782,7 @@ def test__authUserPW_with_embedded_colon(self):
# http://www.zope.org/Collectors/Zope/2039
user_id = 'user'
password = 'embedded:colon'
encoded = base64.encodestring('%s:%s' % (user_id, password))
auth_header = 'basic %s' % encoded
auth_header = basic_auth_encode(user_id, password)

environ = {'HTTP_AUTHORIZATION': auth_header}
request = self._makeOne(environ=environ)
Expand Down
27 changes: 27 additions & 0 deletions src/ZPublisher/utils.py
Expand Up @@ -11,10 +11,12 @@
#
##############################################################################

import base64
import logging
import sys

from Acquisition import aq_inner, aq_parent
from six import PY3
import transaction

if sys.version_info >= (3, ):
Expand Down Expand Up @@ -80,3 +82,28 @@ def safe_unicode(value):
except UnicodeDecodeError:
value = value.decode('utf-8', 'replace')
return value


def basic_auth_encode(user, password=None):
# user / password and the return value are of type str
value = user
if password is not None:
value = value + ':' + password
header = b'Basic ' + base64.b64encode(value.encode('latin-1'))
if PY3:
header = header.decode('latin-1')
return header


def basic_auth_decode(token):
# token and the return values are of type str
if not token:
return None
if not token[:6].lower() == 'basic ':
return None
value = token.split()[-1] # Strip 'Basic '
plain = base64.b64decode(value)
if PY3:
plain = plain.decode('latin-1')
user, password = plain.split(':', 1) # Split at most once
return (user, password)
4 changes: 2 additions & 2 deletions src/ZTUtils/Tree.py
Expand Up @@ -249,12 +249,12 @@ def b2a(s):
text = str(s).translate(a2u_map)
if six.PY3:
text = text.encode('utf-8')
return base64.encodestring(text).replace(b'\n', b'')
return base64.b64encode(text)


def a2b(s):
'''Decode a b2a-encoded string.'''
return base64.decodestring(s.translate(u2a_map))
return base64.b64decode(s.translate(u2a_map))


def encodeExpansion(nodes, compress=1):
Expand Down

0 comments on commit d007655

Please sign in to comment.