def getDbConfig(): """Get the database configuration values from the cherrypy config.""" cfg = config.getConfig() if 'database' in cfg: return cfg['database'] else: return {}
def configureServer(mode=None, plugins=None, curConfig=None): """ Function to setup the cherrypy server. It configures it, but does not actually start it. :param mode: The server mode to start in. :type mode: string :param plugins: If you wish to start the server with a custom set of plugins, pass this as a list of plugins to load. Otherwise, all installed plugins will be loaded. :param curConfig: The configuration dictionary to update. """ if curConfig is None: curConfig = config.getConfig() appconf = { '/': { 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), 'request.show_tracebacks': mode == ServerMode.TESTING, 'request.methods_with_bodies': ('POST', 'PUT', 'PATCH'), 'response.headers.server': 'Girder %s' % __version__, 'error_page.default': _errorDefault } } # Add MIME types for serving Fontello files from staticdir; # these may be missing or incorrect in the OS mimetypes.add_type('application/vnd.ms-fontobject', '.eot') mimetypes.add_type('application/x-font-ttf', '.ttf') mimetypes.add_type('application/font-woff', '.woff') curConfig.update(appconf) if mode: curConfig['server']['mode'] = mode logprint.info('Running in mode: ' + curConfig['server']['mode']) cherrypy.config['engine.autoreload.on'] = mode in [ServerMode.DEVELOPMENT] _setupCache() # Don't import this until after the configs have been read; some module # initialization code requires the configuration to be set up. from girderformindlogger.api import api_main root = webroot.Webroot() api_main.addApiToNode(root) girderformindlogger.events.setupDaemon() routeTable = loadRouteTable() info = { 'config': appconf, 'serverRoot': root, 'serverRootPath': routeTable[constants.GIRDER_ROUTE_ID], 'apiRoot': root.api.v1, } plugin._loadPlugins(info, plugins) root, appconf = info['serverRoot'], info['config'] return root, appconf
def getApiUrl(url=None, preferReferer=False): """ In a request thread, call this to get the path to the root of the REST API. The returned path does *not* end in a forward slash. :param url: URL from which to extract the base URL. If not specified, uses the server root system setting. If that is not specified, uses `cherrypy.url()` :param preferReferer: if no url is specified, this is true, and this is in a cherrypy request that has a referer header that contains the api string, use that referer as the url. """ apiStr = config.getConfig()['server']['api_root'] if not url: if preferReferer and apiStr in cherrypy.request.headers.get('referer', ''): url = cherrypy.request.headers['referer'] else: root = Setting().get(SettingKey.SERVER_ROOT) if root: return posixpath.join(root, apiStr.lstrip('/')) url = url or cherrypy.url() idx = url.find(apiStr) if idx < 0: raise GirderException('Could not determine API root in %s.' % url) return url[:idx + len(apiStr)]
def _setupCache(): """ Setup caching based on configuration file. Cache backends are forcibly replaced because Girder initially configures the regions with the null backends. """ curConfig = config.getConfig() if curConfig['cache']['enabled']: # Replace existing backend, this is necessary # because they're initially configured with the null backend cacheConfig = { 'cache.global.replace_existing_backend': True, 'cache.request.replace_existing_backend': True } curConfig['cache'].update(cacheConfig) cache.configure_from_config(curConfig['cache'], 'cache.global.') requestCache.configure_from_config(curConfig['cache'], 'cache.request.') else: # Reset caches back to null cache (in the case of server teardown) cache.configure(backend='dogpile.cache.null', replace_existing_backend=True) requestCache.configure(backend='dogpile.cache.null', replace_existing_backend=True) # Although the rateLimitBuffer has no pre-existing backend, this method may be called multiple # times in testing (where caches were already configured) rateLimitBuffer.configure(backend='dogpile.cache.memory', replace_existing_backend=True)
def __init__(self, templatePath=None): if not templatePath: templatePath = os.path.join(constants.PACKAGE_DIR, 'api', 'api_docs.mako') super(ApiDocs, self).__init__(templatePath) curConfig = config.getConfig() self.vars['mode'] = curConfig['server'].get('mode', '')
def getConfigurationOption(self, section, key, params): configSection = config.getConfig().get(section) if configSection is None: raise ResourcePathNotFound('No section with that name exists.') elif key not in configSection: raise ResourcePathNotFound('No key with that name exists.') else: return configSection.get(key)
def getRedisConnection(): global _redisClient from redis import Redis, ConnectionPool if _redisClient: return _redisClient cfg = config.getConfig() pool = ConnectionPool(**cfg['redis']) _redisClient = Redis(connection_pool=pool) return _redisClient
def getLogPaths(): """ Return the paths to the error and info log files. These are returned as a dict with "error" and "info" keys that point to the respective file, as well as a "root" key pointing to the log root directory. """ cfg = config.getConfig() logCfg = cfg.get('logging', {}) root = os.path.expanduser(logCfg.get('log_root', LOG_ROOT)) return { 'root': root, 'error': logCfg.get('error_log_file', os.path.join(root, 'error.log')), 'info': logCfg.get('info_log_file', os.path.join(root, 'info.log')) }
def setPassword(self, user, password, save=True): """ Change a user's password. :param user: The user whose password to change. :param password: The new password. If set to None, no password will be stored for this user. This should be done in cases where an external system is responsible for authenticating the user. """ if password is None: user['salt'] = None else: cur_config = config.getConfig() # Normally this would go in validate() but password is a special case. if not re.match(cur_config['users']['password_regex'], password): raise ValidationException(cur_config['users']['password_description'], 'password') user['salt'] = self._cryptContext.hash(password) if save: self.save(user)
def createCipher(applet, appletAssignments, user): thisUser = getCurrentUser() cUser = None try: cUser = UserModel().load(user, level=AccessType.NONE, user=thisUser) except: cur_config = config.getConfig() if not re.match(cur_config['users']['email_regex'], user): raise ValidationException('Invalid email address.', 'user') newCipher = FolderModel().createFolder(parent=applet, name=nextCipher(appletAssignments), parentType='folder', public=False, creator=thisUser, reuseExisting=True) if cUser is None: try: appletName = FolderModel().preferredName(FolderModel().load( applet['meta']['applet']['@id'], level=AccessType.NONE, user=thisUser)['name']) except: raise ValidationException('Invalid assignment folder.', 'applet') try: cUser = UserModel().createUser(login="******".join( [appletName.replace(' ', ''), str(newCipher['name'])]), password=str(uuid.uuid4()), firstName=appletName, email=user, admin=False, public=False, currentUser=thisUser) except: cUser = UserModel().createUser(login="******".join([ appletName.replace(' ', ''), str(applet['meta']['applet']['@id']), str(FolderModel().preferredName(newCipher)) ]), password=str(uuid.uuid4()), firstName=appletName, email=user, admin=False, public=False, currentUser=thisUser) newSecretCipher = FolderModel().setMetadata( FolderModel().createFolder(parent=newCipher, name='userID', parentType='folder', public=False, creator=thisUser, reuseExisting=True), {'user': { '@id': str(cUser['_id']) }}) FolderModel().setAccessList(doc=newSecretCipher, access={ 'users': [], 'groups': [] }, save=True, user=thisUser, force=True) for u in [thisUser, cUser]: FolderModel().setUserAccess(doc=newSecretCipher, user=u, level=None, save=True, currentUser=thisUser, force=True) for u in [thisUser, cUser]: FolderModel().setUserAccess(doc=newSecretCipher, user=u, level=AccessType.READ, save=True, currentUser=thisUser, force=True) return (newCipher)
_originalStdErr = sys.stderr auditLogger = logging.getLogger('girder_audit') auditLogger.setLevel(logging.INFO) logger = logging.getLogger('girderformindlogger') logger.setLevel( logging.DEBUG) # Pass everything; let filters handle level-based filtering config.loadConfig() # Populate the config info at import time # Initialize sentry logging env = os.environ.get('HTTP_HOST', 'localhost') sentry_logging = LoggingIntegration( level=logging.INFO, # Capture info and above as breadcrumbs event_level=logging.CRITICAL # Send errors as events ) if env is not 'localhost': sentry_sdk.init(dsn=config.getConfig()['sentry']['backend_dsn'], environment=env, integrations=[sentry_logging]) class LogLevelFilter(object): """ Filter log records based on whether they are between a min and max level. """ def __init__(self, min, max): self.minLevel = min self.maxLevel = max def filter(self, logRecord): level = logRecord.levelno return self.maxLevel >= level >= self.minLevel
def _attachFileLogHandlers(): """ Sets up the Girder logger. """ global _quiet cfg = config.getConfig() logCfg = cfg.get('logging', {}) # If we are asked to be quiet, set a global flag so that logprint doesn't # have to get the configuration settings every time it is used. if logCfg.get('log_quiet') is True: _quiet = True logPaths = getLogPaths() # Ensure log paths are valid logDirs = [ logPaths['root'], os.path.dirname(logPaths['info']), os.path.dirname(logPaths['error']) ] for logDir in logDirs: mkdir(logDir) # Allow minimum log level to be set via config file level = logging.INFO if logCfg.get('log_level') and isinstance( getattr(logging, logCfg['log_level'], None), int): level = getattr(logging, logCfg['log_level']) logSize = MAX_LOG_SIZE if logCfg.get('log_max_size'): sizeValue = logCfg['log_max_size'] sizeUnits = {'kb': 1024, 'Mb': 1024**2, 'Gb': 1024**3} if sizeValue[-2:] in sizeUnits: logSize = int(sizeValue[:-2].strip()) * sizeUnits[sizeValue[-2:]] else: logSize = int(sizeValue) backupCount = int(logCfg.get('log_backup_count', LOG_BACKUP_COUNT)) fmt = LogFormatter('[%(asctime)s] %(levelname)s: %(message)s') infoMaxLevel = logging.INFO # Create log handlers if logPaths['error'] != logPaths['info']: eh = logging.handlers.RotatingFileHandler(logPaths['error'], maxBytes=logSize, backupCount=backupCount) eh.setLevel(level) eh.addFilter(LogLevelFilter(min=logging.WARNING, max=logging.CRITICAL)) eh._girderLogHandler = 'error' eh.setFormatter(fmt) logger.addHandler(eh) # Record cherrypy errors in our logs, too cherrypy.log.error_log.addHandler(eh) else: infoMaxLevel = logging.CRITICAL if isinstance(getattr(logging, logCfg.get('log_max_info_level', ''), None), int): infoMaxLevel = getattr(logging, logCfg['log_max_info_level']) ih = logging.handlers.RotatingFileHandler(logPaths['info'], maxBytes=logSize, backupCount=backupCount) ih.setLevel(level) ih.addFilter(LogLevelFilter(min=logging.DEBUG, max=infoMaxLevel)) ih._girderLogHandler = 'info' ih.setFormatter(fmt) logger.addHandler(ih) # Record cherrypy errors in our logs, too cherrypy.log.error_log.addHandler(ih) # Log http accesses to the screen and/or the info log. accessLog = logCfg.get('log_access', 'screen') if not isinstance(accessLog, (tuple, list, set)): accessLog = [accessLog] if _quiet or ('screen' not in accessLog and 'stdout' not in accessLog): cherrypy.config.update({'log.screen': False}) if 'info' in accessLog: cherrypy.log.access_log.addHandler(ih) return logger
def setupDaemon(): global daemon if config.getConfig()['server'].get('disable_event_daemon', False): daemon = ForegroundEventsDaemon() else: daemon = AsyncEventsThread()
def getStaticPublicPath(): return config.getConfig()['server']['static_public_path']
def getApiRoot(): return config.getConfig()['server']['api_root']
def __init__(self, templatePath): self.vars = {} self.config = config.getConfig() self._templateDirs = [] self.setTemplatePath(templatePath)
def mountServer(path, database=None, fuseOptions=None, quiet=False, plugins=None): """ Perform the mount. :param path: the mount location. :param database: a database connection URI, if it contains '://'. Otherwise, the default database is used. :param fuseOptions: a comma-separated string of options to pass to the FUSE mount. A key without a value is taken as True. Boolean values are case insensitive. For instance, 'foreground' or 'foreground=True' will keep this program running until the SIGTERM or unmounted. :param quiet: if True, suppress Girder logs. :param plugins: an optional list of plugins to enable. If None, use the plugins that are configured. """ if quiet: curConfig = config.getConfig() curConfig.setdefault('logging', {})['log_quiet'] = True curConfig.setdefault('logging', {})['log_level'] = 'FATAL' girderformindlogger._attachFileLogHandlers() if database and '://' in database: cherrypy.config['database']['uri'] = database if plugins is not None: plugins = plugins.split(',') webroot, appconf = configureServer(plugins=plugins) girderformindlogger._setupCache() opClass = ServerFuse(stat=os.stat(path)) options = { # By default, we run in the background so the mount command returns # immediately. If we run in the foreground, a SIGTERM will shut it # down 'foreground': False, # Cache files if their size and timestamp haven't changed. # This lets the OS buffer files efficiently. 'auto_cache': True, # We aren't specifying our own inos 'use_ino': False, # read-only file system 'ro': True, } if sys.platform != 'darwin': # Automatically unmount when we try to mount again options['auto_unmount'] = True if fuseOptions: for opt in fuseOptions.split(','): if '=' in opt: key, value = opt.split('=', 1) value = (False if value.lower() == 'false' else True if value.lower() == 'true' else value) else: key, value = opt, True if key in ('use_ino', 'ro', 'rw') and options.get(key) != value: logprint.warning('Ignoring the %s=%r option' % (key, value)) continue options[key] = value Setting().set(SettingKey.GIRDER_MOUNT_INFORMATION, { 'path': path, 'mounttime': time.time() }) FUSELogError(opClass, path, **options)