Esempio n. 1
0
    def __call__(self, op, path, *args, **kwargs):
        """
        Generically allow logging and error handling for any operation.

        :param op: operation to perform.
        :param path: path within the fuse (e.g., '', '/user', '/user/<name>',
            etc.).
        """
        logger.debug('-> %s %s %s', op, path, repr(args))
        ret = '[exception]'
        try:
            ret = getattr(self, op)(path, *args, **kwargs)
            return ret
        except Exception as e:
            # Log all exceptions and then reraise them
            if getattr(e, 'errno', None) in (errno.ENOENT, errno.EACCES):
                logger.debug('-- %s %r', op, e)
            else:
                logger.exception('-- %s', op)
            raise e
        finally:
            if op != 'read':
                logger.debug('<- %s %s', op, repr(ret))
            else:
                logger.debug('<- %s (length %d) %r', op, len(ret), ret[:16])
Esempio n. 2
0
    def _getPath(self, path):
        """
        Given a fuse path, return the associated resource.

        :param path: path within the fuse.
        :returns: a Girder resource dictionary.
        """
        # If asked about a file in top level directory or the top directory,
        # return that it doesn't exist.  Other methods should handle '',
        # '/user', and 'collection' before calling this method.
        if '/' not in path.rstrip('/')[1:]:
            raise fuse.FuseOSError(errno.ENOENT)
        try:
            # We can't filter the resource, since that removes files'
            # assetstore information and users' size information.
            resource = path_util.lookUpPath(path.rstrip('/'),
                                            filter=False,
                                            force=True)
        except (path_util.NotFoundException, AccessException):
            raise fuse.FuseOSError(errno.ENOENT)
        except ValidationException:
            raise fuse.FuseOSError(errno.EROFS)
        except Exception:
            logger.exception('ServerFuse server internal error')
            raise fuse.FuseOSError(errno.EROFS)
        return resource  # {model, document}
Esempio n. 3
0
def _ldapAuth(event):
    login, password = event.info['login'], event.info['password']
    servers = Setting().get(PluginSettings.SERVERS)

    for server in servers:
        try:
            # ldap requires a uri complete with protocol.
            # Append one if the user did not specify.
            conn = ldap.initialize(server['uri'])
            conn.set_option(ldap.OPT_TIMEOUT, _CONNECT_TIMEOUT)
            conn.set_option(ldap.OPT_NETWORK_TIMEOUT, _CONNECT_TIMEOUT)
            conn.bind_s(server['bindName'], server['password'], ldap.AUTH_SIMPLE)

            searchStr = '%s=%s' % (server['searchField'], login)
            # Add the searchStr to the attributes, keep local scope.
            lattr = _LDAP_ATTRS + (server['searchField'],)
            results = conn.search_s(server['baseDn'], ldap.SCOPE_SUBTREE, searchStr, lattr)
            if results:
                entry, attrs = results[0]
                dn = attrs['distinguishedName'][0].decode('utf8')
                try:
                    conn.bind_s(dn, password, ldap.AUTH_SIMPLE)
                except ldap.LDAPError:
                    # Try other LDAP servers or fall back to core auth
                    continue
                finally:
                    conn.unbind_s()

                user = _getLdapUser(attrs, server)
                if user:
                    event.stopPropagation().preventDefault().addResponse(user)
        except ldap.LDAPError:
            logger.exception('LDAP connection exception (%s).' % server['uri'])
            continue
Esempio n. 4
0
    def endpointDecorator(self, *path, **params):
        _setCommonCORSHeaders()
        cherrypy.lib.caching.expires(0)
        cherrypy.request.girderRequestUid = str(uuid.uuid4())
        setResponseHeader('Girder-Request-Uid', cherrypy.request.girderRequestUid)

        try:
            val = fun(self, path, params)

            # If this is a partial response, we set the status appropriately
            if 'Content-Range' in cherrypy.response.headers:
                cherrypy.response.status = 206

            val = _mongoCursorToList(val)

            if callable(val):
                # If the endpoint returned anything callable (function,
                # lambda, functools.partial), we assume it's a generator
                # function for a streaming response.
                cherrypy.response.stream = True
                _logRestRequest(self, path, params)
                return val()

            if isinstance(val, cherrypy.lib.file_generator):
                # Don't do any post-processing of static files
                return val

            if isinstance(val, types.GeneratorType):
                val = list(val)

        except RestException as e:
            val = _handleRestException(e)
        except AccessException as e:
            val = _handleAccessException(e)
        except GirderException as e:
            val = _handleGirderException(e)
        except ValidationException as e:
            val = _handleValidationException(e)
        except cherrypy.HTTPRedirect:
            raise
        except Exception:
            # These are unexpected failures; send a 500 status
            logger.exception('500 Error')
            cherrypy.response.status = 500
            val = dict(type='internal', uid=cherrypy.request.girderRequestUid)

            if config.getServerMode() == ServerMode.PRODUCTION:
                # Sanitize errors in production mode
                val['message'] = 'An unexpected error occurred on the server.'
            else:
                # Provide error details in non-production modes
                t, value, tb = sys.exc_info()
                val['message'] = '%s: %s' % (t.__name__, repr(value))
                val['trace'] = traceback.extract_tb(tb)

        resp = _createResponse(val)
        _logRestRequest(self, path, params)

        return resp
Esempio n. 5
0
def _handleGirderException(e):
    # Handle general Girder exceptions
    logger.exception('500 Error')
    cherrypy.response.status = 500
    val = {'message': str(e), 'type': 'girderformindlogger'}
    if e.identifier is not None:
        val['identifier'] = e.identifier
    return val
Esempio n. 6
0
 def wrapped(*args, **kwargs):
     try:
         return fun(*args, **kwargs)
     except ResourcePathNotFound:
         return paramiko.SFTP_NO_SUCH_FILE
     except ValidationException:
         return paramiko.SFTP_FAILURE
     except AccessException:
         return paramiko.SFTP_PERMISSION_DENIED
     except Exception:
         logger.exception('SFTP server internal error')
         return paramiko.SFTP_FAILURE
Esempio n. 7
0
 def capacityInfo(self):
     """
     For filesystem assetstores, we just need to report the free and total
     space on the filesystem where the assetstore lives.
     """
     try:
         usage = psutil.disk_usage(self.assetstore['root'])
         return {'free': usage.free, 'total': usage.total}
     except OSError:
         logger.exception(
             'Failed to get disk usage of %s' % self.assetstore['root'])
     # If psutil.disk_usage fails or we can't query the assetstore's root
     # directory, just report nothing regarding disk capacity
     return {
         'free': None,
         'total': None
     }
Esempio n. 8
0
 def __init__(self, assetstore):
     super(FilesystemAssetstoreAdapter, self).__init__(assetstore)
     # If we can't create the temp directory, the assetstore still needs to
     # be initialized so that it can be deleted or modified.  The validation
     # prevents invalid new assetstores from being created, so this only
     # happens to existing assetstores that no longer can access their temp
     # directories.
     self.tempDir = os.path.join(self.assetstore.get('root', '/'), 'temp')
     try:
         mkdir(self.tempDir)
     except OSError:
         self.unavailable = True
         logger.exception('Failed to create filesystem assetstore '
                          'directories %s' % self.tempDir)
     if not os.access(self.assetstore['root'], os.W_OK):
         self.unavailable = True
         logger.error('Could not write to assetstore root: %s',
                      self.assetstore['root'])
 def __init__(self, assetstore):
     """
     :param assetstore: The assetstore to act on.
     """
     super(GridFsAssetstoreAdapter, self).__init__(assetstore)
     recent = False
     try:
         # Guard in case the connectionArgs is unhashable
         key = (self.assetstore.get('mongohost'),
                self.assetstore.get('replicaset'))
         if key in _recentConnections:
             recent = (time.time() - _recentConnections[key]['created']
                       < RECENT_CONNECTION_CACHE_TIME)
     except TypeError:
         key = None
     try:
         # MongoClient automatically reuses connections from a pool, but we
         # want to avoid redoing ensureChunkIndices each time we get such a
         # connection.
         client = getDbConnection(self.assetstore.get('mongohost'),
                                  self.assetstore.get('replicaset'),
                                  quiet=recent)
         self.chunkColl = MongoProxy(client[self.assetstore['db']].chunk)
         if not recent:
             _ensureChunkIndices(self.chunkColl)
             if key is not None:
                 if len(_recentConnections) >= RECENT_CONNECTION_CACHE_MAX_SIZE:
                     _recentConnections.clear()
                 _recentConnections[key] = {
                     'created': time.time()
                 }
     except pymongo.errors.ConnectionFailure:
         logger.error('Failed to connect to GridFS assetstore %s',
                      self.assetstore['db'])
         self.chunkColl = 'Failed to connect'
         self.unavailable = True
     except pymongo.errors.ConfigurationError:
         logger.exception('Failed to configure GridFS assetstore %s',
                          self.assetstore['db'])
         self.chunkColl = 'Failed to configure'
         self.unavailable = True
Esempio n. 10
0
    def validateInfo(doc):
        """
        Makes sure the root field is a valid absolute path and is writeable.
        It also conveniently update the root field replacing the initial
        component by the user home directory running the server if it matches
        ``~`` or ``~user``.
        """
        doc['root'] = os.path.expanduser(doc['root'])

        if not os.path.isabs(doc['root']):
            raise ValidationException('You must provide an absolute path '
                                      'for the root directory.', 'root')

        try:
            mkdir(doc['root'])
        except OSError:
            msg = 'Could not make directory "%s".' % doc['root']
            logger.exception(msg)
            raise ValidationException(msg)
        if not os.access(doc['root'], os.W_OK):
            raise ValidationException(
                'Unable to write into directory "%s".' % doc['root'])

        if not doc.get('perms'):
            doc['perms'] = DEFAULT_PERMS
        else:
            try:
                perms = doc['perms']
                if not isinstance(perms, int):
                    perms = int(doc['perms'], 8)

                # Make sure that mode is still rw for user
                if not perms & stat.S_IRUSR or not perms & stat.S_IWUSR:
                    raise ValidationException(
                        'File permissions must allow "rw" for user.')
                doc['perms'] = perms
            except ValueError:
                raise ValidationException(
                    'File permissions must be an octal integer.')
Esempio n. 11
0
    def deleteFile(self, file):
        """
        Deletes the file from disk if it is the only File in this assetstore
        with the given sha512. Imported files are not actually deleted.
        """
        from girderformindlogger.models.file import File

        if file.get('imported') or 'path' not in file:
            return

        q = {
            'sha512': file['sha512'],
            'assetstoreId': self.assetstore['_id']
        }
        path = os.path.join(self.assetstore['root'], file['path'])
        if os.path.isfile(path):
            with filelock.FileLock(path + '.deleteLock'):
                matching = File().find(q, limit=2, fields=[])
                matchingUpload = Upload().findOne(q)
                if matching.count(True) == 1 and matchingUpload is None:
                    try:
                        os.unlink(path)
                    except Exception:
                        logger.exception('Failed to delete file %s' % path)