diff -r 26491ee91e33 -r e4cb9c53db3e thirdparty/google_appengine/google/appengine/tools/dev_appserver.py --- a/thirdparty/google_appengine/google/appengine/tools/dev_appserver.py Tue Apr 21 16:28:13 2009 +0000 +++ b/thirdparty/google_appengine/google/appengine/tools/dev_appserver.py Fri Apr 24 14:16:00 2009 +0000 @@ -39,6 +39,12 @@ import cStringIO import cgi import cgitb + +try: + import distutils.util +except ImportError: + pass + import dummy_thread import email.Utils import errno @@ -258,17 +264,17 @@ access the URL; False if anyone can access the URL. """ if not isinstance(dispatcher, URLDispatcher): - raise TypeError, 'dispatcher must be a URLDispatcher sub-class' + raise TypeError('dispatcher must be a URLDispatcher sub-class') if regex.startswith('^') or regex.endswith('$'): - raise InvalidAppConfigError, 'regex starts with "^" or ends with "$"' + raise InvalidAppConfigError('regex starts with "^" or ends with "$"') adjusted_regex = '^%s$' % regex try: url_re = re.compile(adjusted_regex) except re.error, e: - raise InvalidAppConfigError, 'regex invalid: %s' % e + raise InvalidAppConfigError('regex invalid: %s' % e) match_tuple = (url_re, dispatcher, path, requires_login, admin_only) self._url_patterns.append(match_tuple) @@ -348,7 +354,7 @@ path variable supplied to this method is ignored. """ cookies = ', '.join(headers.getheaders('cookie')) - email, admin = self._get_user_info(cookies) + email, admin, user_id = self._get_user_info(cookies) for matcher in self._url_matchers: dispatcher, matched_path, requires_login, admin_only = matcher.Match(relative_url) @@ -540,8 +546,9 @@ env['CONTENT_LENGTH'] = headers.getheader('content-length', '') cookies = ', '.join(headers.getheaders('cookie')) - email, admin = get_user_info(cookies) + email, admin, user_id = get_user_info(cookies) env['USER_EMAIL'] = email + env['USER_ID'] = user_id if admin: env['USER_IS_ADMIN'] = '1' @@ -615,14 +622,14 @@ def FakeUnlink(path): """Fake version of os.unlink.""" if os.path.isdir(path): - raise OSError(2, "Is a directory", path) + raise OSError(errno.ENOENT, "Is a directory", path) else: - raise OSError(1, "Operation not permitted", path) + raise OSError(errno.EPERM, "Operation not permitted", path) def FakeReadlink(path): """Fake version of os.readlink.""" - raise OSError(22, "Invalid argument", path) + raise OSError(errno.EINVAL, "Invalid argument", path) def FakeAccess(path, mode): @@ -636,10 +643,33 @@ def FakeSetLocale(category, value=None, original_setlocale=locale.setlocale): """Fake version of locale.setlocale that only supports the default.""" if value not in (None, '', 'C', 'POSIX'): - raise locale.Error, 'locale emulation only supports "C" locale' + raise locale.Error('locale emulation only supports "C" locale') return original_setlocale(category, 'C') +def FakeOpen(file, flags, mode=0777): + """Fake version of os.open.""" + raise OSError(errno.EPERM, "Operation not permitted", file) + + +def FakeRename(src, dst): + """Fake version of os.rename.""" + raise OSError(errno.EPERM, "Operation not permitted", src) + + +def FakeUTime(path, times): + """Fake version of os.utime.""" + raise OSError(errno.EPERM, "Operation not permitted", path) + + +def FakeGetPlatform(): + """Fake distutils.util.get_platform on OS/X. Pass-through otherwise.""" + if sys.platform == 'darwin': + return 'macosx-' + else: + return distutils.util.get_platform() + + def IsPathInSubdirectories(filename, subdirectories, normcase=os.path.normcase): @@ -739,6 +769,21 @@ return output_dict +def GeneratePythonPaths(*p): + """Generate all valid filenames for the given file + + Args: + p: Positional args are the folders to the file and finally the file + without a suffix. + + Returns: + A list of strings representing the given path to a file with each valid + suffix for this python build. + """ + suffixes = imp.get_suffixes() + return [os.path.join(*p) + s for s, m, t in suffixes] + + class FakeFile(file): """File sub-class that enforces the security restrictions of the production environment. @@ -771,6 +816,50 @@ ]) + ALLOWED_SITE_PACKAGE_FILES = set( + os.path.normcase(os.path.abspath(os.path.join( + os.path.dirname(os.__file__), 'site-packages', path))) + for path in itertools.chain(*[ + + [os.path.join('Crypto')], + GeneratePythonPaths('Crypto', '__init__'), + [os.path.join('Crypto', 'Cipher')], + GeneratePythonPaths('Crypto', 'Cipher', '__init__'), + GeneratePythonPaths('Crypto', 'Cipher', 'AES'), + GeneratePythonPaths('Crypto', 'Cipher', 'ARC2'), + GeneratePythonPaths('Crypto', 'Cipher', 'ARC4'), + GeneratePythonPaths('Crypto', 'Cipher', 'Blowfish'), + GeneratePythonPaths('Crypto', 'Cipher', 'CAST'), + GeneratePythonPaths('Crypto', 'Cipher', 'DES'), + GeneratePythonPaths('Crypto', 'Cipher', 'DES3'), + GeneratePythonPaths('Crypto', 'Cipher', 'XOR'), + [os.path.join('Crypto', 'Hash')], + GeneratePythonPaths('Crypto', 'Hash', '__init__'), + GeneratePythonPaths('Crypto', 'Hash', 'HMAC'), + os.path.join('Crypto', 'Hash', 'MD2'), + os.path.join('Crypto', 'Hash', 'MD4'), + GeneratePythonPaths('Crypto', 'Hash', 'MD5'), + GeneratePythonPaths('Crypto', 'Hash', 'SHA'), + os.path.join('Crypto', 'Hash', 'SHA256'), + os.path.join('Crypto', 'Hash', 'RIPEMD'), + [os.path.join('Crypto', 'Protocol')], + GeneratePythonPaths('Crypto', 'Protocol', '__init__'), + GeneratePythonPaths('Crypto', 'Protocol', 'AllOrNothing'), + GeneratePythonPaths('Crypto', 'Protocol', 'Chaffing'), + [os.path.join('Crypto', 'PublicKey')], + GeneratePythonPaths('Crypto', 'PublicKey', '__init__'), + GeneratePythonPaths('Crypto', 'PublicKey', 'DSA'), + GeneratePythonPaths('Crypto', 'PublicKey', 'ElGamal'), + GeneratePythonPaths('Crypto', 'PublicKey', 'RSA'), + GeneratePythonPaths('Crypto', 'PublicKey', 'pubkey'), + GeneratePythonPaths('Crypto', 'PublicKey', 'qNEW'), + [os.path.join('Crypto', 'Util')], + GeneratePythonPaths('Crypto', 'Util', '__init__'), + GeneratePythonPaths('Crypto', 'Util', 'RFC1751'), + GeneratePythonPaths('Crypto', 'Util', 'number'), + GeneratePythonPaths('Crypto', 'Util', 'randpool'), + ])) + _original_file = file _root_path = None @@ -863,9 +952,6 @@ """ logical_filename = normcase(os.path.abspath(filename)) - if os.path.isdir(logical_filename): - logical_filename = os.path.join(logical_filename, 'foo') - result = FakeFile._availability_cache.get(logical_filename) if result is None: result = FakeFile._IsFileAccessibleNoCache(logical_filename, @@ -886,9 +972,13 @@ Returns: True if the file is accessible, False otherwise. """ - if IsPathInSubdirectories(logical_filename, [FakeFile._root_path], + logical_dirfakefile = logical_filename + if os.path.isdir(logical_filename): + logical_dirfakefile = os.path.join(logical_filename, 'foo') + + if IsPathInSubdirectories(logical_dirfakefile, [FakeFile._root_path], normcase=normcase): - relative_filename = logical_filename[len(FakeFile._root_path):] + relative_filename = logical_dirfakefile[len(FakeFile._root_path):] if (not FakeFile._allow_skipped_files and FakeFile._skip_files.match(relative_filename)): @@ -904,16 +994,19 @@ if logical_filename in FakeFile.ALLOWED_FILES: return True - if IsPathInSubdirectories(logical_filename, + if logical_filename in FakeFile.ALLOWED_SITE_PACKAGE_FILES: + return True + + if IsPathInSubdirectories(logical_dirfakefile, FakeFile.ALLOWED_SITE_PACKAGE_DIRS, normcase=normcase): return True allowed_dirs = FakeFile._application_paths | FakeFile.ALLOWED_DIRS - if (IsPathInSubdirectories(logical_filename, + if (IsPathInSubdirectories(logical_dirfakefile, allowed_dirs, normcase=normcase) and - not IsPathInSubdirectories(logical_filename, + not IsPathInSubdirectories(logical_dirfakefile, FakeFile.NOT_ALLOWED_DIRS, normcase=normcase)): return True @@ -926,7 +1019,7 @@ raise IOError('invalid mode: %s' % mode) if not FakeFile.IsFileAccessible(filename): - raise IOError(errno.EACCES, 'file not accessible') + raise IOError(errno.EACCES, 'file not accessible', filename) super(FakeFile, self).__init__(filename, mode, bufsize, **kwargs) @@ -950,7 +1043,7 @@ """Enforces access permissions for the function passed to the constructor. """ if not FakeFile.IsFileAccessible(path): - raise OSError(errno.EACCES, 'path not accessible') + raise OSError(errno.EACCES, 'path not accessible', path) return self._original_func(path, *args, **kwargs) @@ -1035,6 +1128,31 @@ print >>sys.stderr, indent + (message % args) _WHITE_LIST_C_MODULES = [ + 'AES', + 'ARC2', + 'ARC4', + 'Blowfish', + 'CAST', + 'DES', + 'DES3', + 'MD2', + 'MD4', + 'RIPEMD', + 'SHA256', + 'XOR', + + '_Crypto_Cipher__AES', + '_Crypto_Cipher__ARC2', + '_Crypto_Cipher__ARC4', + '_Crypto_Cipher__Blowfish', + '_Crypto_Cipher__CAST', + '_Crypto_Cipher__DES', + '_Crypto_Cipher__DES3', + '_Crypto_Cipher__XOR', + '_Crypto_Hash__MD2', + '_Crypto_Hash__MD4', + '_Crypto_Hash__RIPEMD', + '_Crypto_Hash__SHA256', 'array', 'binascii', 'bz2', @@ -1089,7 +1207,24 @@ '__main__', ] + __CRYPTO_CIPHER_ALLOWED_MODULES = [ + 'MODE_CBC', + 'MODE_CFB', + 'MODE_CTR', + 'MODE_ECB', + 'MODE_OFB', + 'block_size', + 'key_size', + 'new', + ] _WHITE_LIST_PARTIAL_MODULES = { + 'Crypto.Cipher.AES': __CRYPTO_CIPHER_ALLOWED_MODULES, + 'Crypto.Cipher.ARC2': __CRYPTO_CIPHER_ALLOWED_MODULES, + 'Crypto.Cipher.Blowfish': __CRYPTO_CIPHER_ALLOWED_MODULES, + 'Crypto.Cipher.CAST': __CRYPTO_CIPHER_ALLOWED_MODULES, + 'Crypto.Cipher.DES': __CRYPTO_CIPHER_ALLOWED_MODULES, + 'Crypto.Cipher.DES3': __CRYPTO_CIPHER_ALLOWED_MODULES, + 'gc': [ 'enable', 'disable', @@ -1149,12 +1284,14 @@ 'O_SYNC', 'O_TRUNC', 'O_WRONLY', + 'open', 'pardir', 'path', 'pathsep', 'R_OK', 'readlink', 'remove', + 'rename', 'SEEK_CUR', 'SEEK_END', 'SEEK_SET', @@ -1166,6 +1303,7 @@ 'TMP_MAX', 'unlink', 'urandom', + 'utime', 'walk', 'WCOREDUMP', 'WEXITSTATUS', @@ -1191,12 +1329,19 @@ 'listdir': RestrictedPathFunction(os.listdir), 'lstat': RestrictedPathFunction(os.stat), + 'open': FakeOpen, 'readlink': FakeReadlink, 'remove': FakeUnlink, + 'rename': FakeRename, 'stat': RestrictedPathFunction(os.stat), 'uname': FakeUname, 'unlink': FakeUnlink, 'urandom': FakeURandom, + 'utime': FakeUTime, + }, + + 'distutils.util': { + 'get_platform': FakeGetPlatform, }, } @@ -1483,7 +1628,6 @@ module.__name__ = 'cPickle' elif submodule_fullname == 'os': module.__dict__.update(self._os.__dict__) - self._module_dict['os.path'] = module.path elif self.StubModuleExists(submodule_fullname): module = self.ImportStubModule(submodule_fullname) else: @@ -1498,6 +1642,12 @@ if submodule_fullname not in self._module_dict: self._module_dict[submodule_fullname] = module + if submodule_fullname == 'os': + os_path_name = module.path.__name__ + os_path = self.FindAndLoadModule(os_path_name, os_path_name, search_path) + self._module_dict['os.path'] = os_path + module.__dict__['path'] = os_path + return module @Trace @@ -3013,6 +3163,9 @@ enable_sendmail: Whether to use sendmail as an alternative to SMTP. show_mail_body: Whether to log the body of emails. remove: Used for dependency injection. + trusted: True if this app can access data belonging to other apps. This + behavior is different from the real app server and should be left False + except for advanced uses of dev_appserver. """ login_url = config['login_url'] datastore_path = config['datastore_path'] @@ -3026,6 +3179,7 @@ enable_sendmail = config.get('enable_sendmail', False) show_mail_body = config.get('show_mail_body', False) remove = config.get('remove', os.remove) + trusted = config.get('trusted', False) os.environ['APPLICATION_ID'] = app_id @@ -3041,7 +3195,8 @@ apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap() datastore = datastore_file_stub.DatastoreFileStub( - app_id, datastore_path, history_path, require_indexes=require_indexes) + app_id, datastore_path, history_path, require_indexes=require_indexes, + trusted=trusted) apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', datastore) fixed_login_url = '%s?%s=%%s' % (login_url,