Esempio n. 1
0
    def validate(self, doc):
        """
        Validate the user every time it is stored in the database.
        """
        doc['email'] = doc.get('email', '').lower().strip()

        # if 'salt' not in doc:
        #     # Internal error, this should not happen
        #     raise Exception('Tried to save user document with no salt.')
        #
        # if 'hashAlg' in doc:
        #     # This is a legacy field; hash algorithms are now inline with the password hash
        #     del doc['hashAlg']

        if not doc.get('email_encrypted',
                       None) and not mail_utils.validateEmailAddress(
                           doc['email']):
            raise ValidationException('Invalid email address.', 'email')

        # Ensure unique emails # TO DO: use existing user if email exists
        q = {'email': doc['email']}
        if '_id' in doc:
            q['_id'] = {'$ne': doc['_id']}
        existing = self.findOne(q)
        if existing is not None:
            raise ValidationException(
                ''.join([
                    'That email is already registered:',
                    str(existing["_id"])
                ]), 'email')

        return doc
Esempio n. 2
0
 def _validateBannerColor(doc):
     if not doc['value']:
         raise ValidationException('The banner color may not be empty',
                                   'value')
     elif not (re.match(r'^#[0-9A-Fa-f]{6}$', doc['value'])):
         raise ValidationException(
             'The banner color must be a hex color triplet', 'value')
Esempio n. 3
0
    def validate(self, doc):
        # Ensure no duplicate names
        q = {'name': doc['name']}
        if '_id' in doc:
            q['_id'] = {'$ne': doc['_id']}
        duplicate = self.findOne(q, fields=['_id'])
        if duplicate is not None:
            raise ValidationException(
                'An assetstore with that name already '
                'exists.', 'name')

        # Name must not be empty
        if not doc['name']:
            raise ValidationException('Name must not be empty.', 'name')

        # Adapter classes validate each type internally
        adapter = assetstore_utilities.getAssetstoreAdapter(doc,
                                                            instance=False)
        adapter.validateInfo(doc)

        # If no current assetstore exists yet, set this one as the current.
        current = self.findOne({'current': True}, fields=['_id'])
        if current is None:
            doc['current'] = True
        if 'current' not in doc:
            doc['current'] = False

        # If we are setting this as current, we should unmark all other
        # assetstores that have the current flag.
        if doc['current'] is True:
            self.update({'current': True}, {'$set': {'current': False}})

        return doc
Esempio n. 4
0
    def validate(self, doc):
        from .token import Token
        from .user import User

        if doc['tokenDuration']:
            doc['tokenDuration'] = float(doc['tokenDuration'])
        else:
            doc['tokenDuration'] = None

        doc['name'] = doc['name'].strip()
        doc['active'] = bool(doc.get('active', True))

        if doc['scope'] is not None:
            if not isinstance(doc['scope'], (list, tuple)):
                raise ValidationException('Scope must be a list, or None.')
            if not doc['scope']:
                raise ValidationException(
                    'Custom scope list must not be empty.')

            # Ensure only registered scopes are being set
            admin = User().load(doc['userId'], force=True)['admin']
            scopes = TokenScope.scopeIds(admin)
            unknownScopes = set(doc['scope']) - scopes
            if unknownScopes:
                raise ValidationException('Invalid scopes: %s.' %
                                          ','.join(unknownScopes))

        # Deactivating an already existing token
        if '_id' in doc and not doc['active']:
            Token().clearForApiKey(doc)

        return doc
Esempio n. 5
0
    def validate(self, doc):
        """
        Validate the user every time it is stored in the database.
        """
        for s in ['email', 'displayName', 'firstName']:
            if s in doc and doc[s] is None:
                doc[s] = ''
        doc['login'] = doc.get('login', '').lower().strip()
        doc['email'] = doc.get('email', '').lower().strip()
        doc['displayName'] = doc.get(
            'displayName',
            doc.get('firstName', '')
        ).strip()
        doc['firstName'] = doc.get('firstName', '').strip()
        doc['status'] = doc.get('status', 'enabled')
        doc['deviceId'] = doc.get('deviceId', '')

        if 'salt' not in doc:
            # Internal error, this should not happen
            raise Exception('Tried to save user document with no salt.')

        if not doc['displayName']:
            raise ValidationException('Display name must not be empty.',
                                      'displayName')

        if doc['status'] not in ('pending', 'enabled', 'disabled'):
            raise ValidationException(
                'Status must be pending, enabled, or disabled.', 'status')

        if 'hashAlg' in doc:
            # This is a legacy field; hash algorithms are now inline with the password hash
            del doc['hashAlg']

        self._validateLogin(doc['login'])

        if len(doc['email']) and not mail_utils.validateEmailAddress(
            doc['email']
        ):
            raise ValidationException('Invalid email address.', 'email')

        # Ensure unique logins
        q = {'login': doc['login']}
        if '_id' in doc:
            q['_id'] = {'$ne': doc['_id']}
        existing = self.findOne(q)
        if existing is not None:
            raise ValidationException('That login is already registered.',
                                      'login')

        # If this is the first user being created, make it an admin
        existing = self.findOne({})
        if existing is None:
            doc['admin'] = True
            # Ensure settings don't stop this user from logging in
            doc['emailVerified'] = True
            doc['status'] = 'enabled'

        return doc
Esempio n. 6
0
    def validate(self, doc):
        if doc['size'] < 0:
            raise ValidationException('File size must not be negative.')
        if doc['received'] > doc['size']:
            raise ValidationException('Received too many bytes.')

        doc['updated'] = datetime.datetime.utcnow()

        return doc
Esempio n. 7
0
    def _validateLogin(self, login):
        if '@' in login:
            # Hard-code this constraint so we can always easily distinguish
            # an email address from a login
            raise ValidationException('Login may not contain "@".', 'login')

        if not re.match(r'^[a-z][\da-z\-\.]{3,}$', login):
            raise ValidationException(
                'Login must be at least 4 characters, start with a letter, and may only contain '
                'letters, numbers, dashes, and dots.', 'login')
Esempio n. 8
0
    def _validateRouteTable(doc):
        nonEmptyRoutes = [route for route in doc['value'].values() if route]
        if GIRDER_ROUTE_ID not in doc[
                'value'] or not doc['value'][GIRDER_ROUTE_ID]:
            raise ValidationException('Girder root must be routable.', 'value')

        for key in doc['value']:
            if (doc['value'][key] and not doc['value'][key].startswith('/')):
                raise ValidationException(
                    'Routes must begin with a forward slash.', 'value')

        if len(nonEmptyRoutes) > len(set(nonEmptyRoutes)):
            raise ValidationException('Routes must be unique.', 'value')
Esempio n. 9
0
def _validateLogo(doc):
    try:
        logoFile = File().load(doc['value'],
                               level=AccessType.READ,
                               user=None,
                               exc=True)
    except ValidationException as e:
        # Invalid ObjectId, or non-existent document
        raise ValidationException(str(e), 'value')
    except AccessException:
        raise ValidationException('Logo must be publicly readable', 'value')

    # Store this field natively as an ObjectId
    doc['value'] = logoFile['_id']
Esempio n. 10
0
    def importData(self, parent, parentType, params, progress, user, leafFoldersAsItems):
        importPath = params['importPath']

        if not os.path.exists(importPath):
            raise ValidationException('Not found: %s.' % importPath)
        if not os.path.isdir(importPath):
            name = os.path.basename(importPath)
            progress.update(message=name)
            self._importFileToFolder(name, user, parent, parentType, importPath)
            return

        listDir = os.listdir(importPath)

        if parentType != 'folder' and any(
                os.path.isfile(os.path.join(importPath, val)) for val in listDir):
            raise ValidationException(
                'Files cannot be imported directly underneath a %s.' % parentType)

        if leafFoldersAsItems and self._hasOnlyFiles(importPath, listDir):
            self._importDataAsItem(
                os.path.basename(importPath.rstrip(os.sep)), user, parent, importPath,
                listDir, params=params)
            return

        for name in listDir:
            progress.update(message=name)
            path = os.path.join(importPath, name)

            if os.path.isdir(path):
                localListDir = os.listdir(path)
                if leafFoldersAsItems and self._hasOnlyFiles(path, localListDir):
                    self._importDataAsItem(name, user, parent, path, localListDir, params=params)
                else:
                    folder = Folder().createFolder(
                        parent=parent, name=name, parentType=parentType,
                        creator=user, reuseExisting=True)
                    events.trigger(
                        'filesystem_assetstore_imported', {
                            'id': folder['_id'],
                            'type': 'folder',
                            'importPath': path
                        })
                    nextPath = os.path.join(importPath, name)
                    self.importData(
                        folder, 'folder', params=dict(params, importPath=nextPath),
                        progress=progress, user=user, leafFoldersAsItems=leafFoldersAsItems)
            else:
                if self.shouldImportFile(path, params):
                    self._importFileToFolder(name, user, parent, parentType, path)
Esempio n. 11
0
 def _verify_parentType(self, parentType):
     parentType = parentType.lower()
     if parentType not in ('folder', 'user', 'collection', 'profile'):
         raise ValidationException(
             'The parentType must be folder, collection, user, or profile.'
         )
     return(parentType)
Esempio n. 12
0
    def inviteUser(self, group, user, level=AccessType.READ):
        """
        Invite a user to join the group. Inviting them automatically
        grants the user read access to the group so that they can see it.
        Once they accept the invitation, they will be given the specified level
        of access.

        If the user has requested an invitation to this group, calling this
        will accept their request and add them to the group at the access
        level specified.
        """
        from girderformindlogger.models.user import User

        if group['_id'] in user.get('groups', []):
            raise ValidationException('User is already in this group.')

        # If there is an outstanding request to join from this user, we
        # just add them to the group instead of invite them.
        if user['_id'] in group.get('requests', []):
            return self.addUser(group, user, level)

        if 'groupInvites' not in user:
            user['groupInvites'] = []

        for invite in user['groupInvites']:
            if invite['groupId'] == group['_id']:
                invite['level'] = level
                break
        else:
            user['groupInvites'].append({
                'groupId': group['_id'],
                'level': level
            })

        return User().save(user, validate=False)
Esempio n. 13
0
 def _validateContactEmailAddress(doc):
     # This is typically used within an RFC 6068 "mailto:" scheme, so no display name is allowed
     from girderformindlogger.utility.mail_utils import validateEmailAddress
     if not validateEmailAddress(doc['value']):
         raise ValidationException(
             'Contact email address must be a valid email address.',
             'value')
Esempio n. 14
0
def _validateServers(doc):
    serversSchema = {
        'type': 'array',
        'items': {
            'type': 'object',
            'properties': {
                'uri': {
                    'type': 'string',
                    'minLength': 1
                },
                'bindName': {
                    'type': 'string',
                    'minLength': 1
                },
                'baseDn': {
                    'type': 'string'
                }
            },
            'required': ['uri', 'bindName', 'baseDn']
        }
    }
    try:
        jsonschema.validate(doc['value'], serversSchema)
    except jsonschema.ValidationError as e:
        raise ValidationException('Invalid LDAP servers list: ' + str(e))

    for server in doc['value']:
        if '://' not in server['uri']:
            server['uri'] = 'ldap://' + server['uri']
        server['password'] = server.get('password', '')
        server['searchField'] = server.get('searchField', 'uid')
Esempio n. 15
0
 def _validateAddToGroupPolicy(doc):
     doc['value'] = doc['value'].lower()
     if doc['value'] not in ('never', 'noadmin', 'nomod', 'yesadmin',
                             'yesmod', ''):
         raise ValidationException(
             'Add to group policy must be one of "never", "noadmin", '
             '"nomod", "yesadmin", or "yesmod".', 'value')
Esempio n. 16
0
def checkOauthUser(event):
    """
    If an OAuth2 user without a password tries to log in with a password, we
    want to give them a useful error message.
    """
    user = event.info['user']
    if user.get('oauth'):
        if isinstance(user['oauth'], dict):
            # Handle a legacy format where only 1 provider (Google) was stored
            userProviders = {'google'}
        else:
            userProviders = {val['provider'] for val in user['oauth']}

        # Silently skip non-installed providers
        availableUserProviders = userProviders & set(providers.idMap.keys())

        if availableUserProviders:
            prettyProviderNames = ', '.join(sorted(
                providers.idMap[userProvider].getProviderName(external=True)
                for userProvider in availableUserProviders
            ))
            helpMessage = 'Please log in with %s, or use the password reset link.' % \
                          prettyProviderNames
        else:
            helpMessage = 'Please use the password reset link.'

        raise ValidationException(
            "You don't have a password. %s" % helpMessage)
Esempio n. 17
0
def checkObjectForURLs(obj):
    """
    Recursively checks if a dict is keyed with 'url'. If so, it checks for
    language encoding.

    inputs
    ------
    obj: dict, list, string, or None

    returns
    -------
    None
    """
    if isinstance(obj, list):
        [checkObjectForURLs(o) for o in obj]
    elif isinstance(obj, dict):
        languageEncodedURLs = [
            obj[k] for k in obj.keys()
            if k.split('/')[-1].split(':')[-1].lower() == "url"
            and checkForLanguageEncodedURL(obj[k])
        ]
        if any(languageEncodedURLs):
            raise ValidationException("Language-encoded URL: {}".format(
                str(languageEncodedURLs)))
        [checkObjectForURLs(obj[k]) for k in obj.keys()]
    return (None)
Esempio n. 18
0
def updateItemLicense(event):
    """
    REST event handler to update item with license parameter, if provided.
    """
    params = event.info['params']
    if 'license' not in params:
        return

    itemModel = Item()
    item = itemModel.load(event.info['returnVal']['_id'], force=True, exc=True)
    newLicense = validateString(params['license'])
    if item['license'] == newLicense:
        return

    # Ensure that new license name is in configured list of licenses.
    #
    # Enforcing this here, instead of when validating the item, avoids an extra
    # database lookup (for the settings) on every future item save.
    if newLicense:
        licenseSetting = Setting().get(PluginSettings.LICENSES)
        validLicense = any(
            license['name'] == newLicense
            for group in licenseSetting
            for license in group['licenses'])
        if not validLicense:
            raise ValidationException(
                'License name must be in configured list of licenses.', 'license')

    item['license'] = newLicense
    item = itemModel.save(item)
    event.preventDefault()
    event.addResponse(item)
Esempio n. 19
0
    def createToken(self, key, days=None):
        """
        Create a token using an API key.

        :param key: The API key (the key itself, not the full document).
        :type key: str
        :param days: You may request a token duration up to the token duration
            of the API key itself, or pass None to use the API key duration.
        :type days: float or None
        """
        from .setting import Setting
        from .token import Token
        from .user import User

        apiKey = self.findOne({'key': key})

        if apiKey is None or not apiKey['active']:
            raise ValidationException('Invalid API key.')

        cap = apiKey['tokenDuration'] or Setting().get(
            SettingKey.COOKIE_LIFETIME)
        days = min(float(days or cap), cap)

        user = User().load(apiKey['userId'], force=True)

        # Mark last used stamp
        apiKey['lastUse'] = datetime.datetime.utcnow()
        apiKey = self.save(apiKey)
        token = Token().createToken(user=user,
                                    days=days,
                                    scope=apiKey['scope'],
                                    apiKey=apiKey)
        return (user, token)
Esempio n. 20
0
    def remove(self, assetstore, **kwargs):
        """
        Delete an assetstore. If there are any files within this assetstore,
        a validation exception is raised.

        :param assetstore: The assetstore document to delete.
        :type assetstore: dict
        """
        from girderformindlogger.models.file import File

        files = File().findOne({'assetstoreId': assetstore['_id']})
        if files is not None:
            raise ValidationException(
                'You may not delete an assetstore that contains files.')
        # delete partial uploads before we delete the store.
        adapter = assetstore_utilities.getAssetstoreAdapter(assetstore)
        try:
            adapter.untrackedUploads([], delete=True)
        except ValidationException:
            # this assetstore is currently unreachable, so skip this step
            pass
        # now remove the assetstore
        Model.remove(self, assetstore)
        # If after removal there is no current assetstore, then pick a
        # different assetstore to be the current one.
        current = self.findOne({'current': True})
        if current is None:
            first = self.findOne(sort=[('created', SortDir.DESCENDING)])
            if first is not None:
                first['current'] = True
                self.save(first)
Esempio n. 21
0
 def _validateSmtpPort(doc):
     try:
         doc['value'] = int(doc['value'])
         if doc['value'] > 0:
             return
     except ValueError:
         pass  # We want to raise the ValidationException
     raise ValidationException('SMTP port must be an integer > 0.', 'value')
Esempio n. 22
0
def _validateSettings(doc):
    from .quota import ValidateSizeQuota
    val = doc['value']

    val, err = ValidateSizeQuota(val)
    if err:
        raise ValidationException(err, 'value')
    doc['value'] = val
Esempio n. 23
0
 def _validateCookieLifetime(doc):
     try:
         doc['value'] = int(doc['value'])
         if doc['value'] > 0:
             return
     except ValueError:
         pass  # We want to raise the ValidationException
     raise ValidationException('Cookie lifetime must be an integer > 0.',
                               'value')
Esempio n. 24
0
    def copyFolder(self, srcFolder, parent=None, name=None, description=None,
                   parentType=None, public=None, creator=None, progress=None,
                   firstFolder=None):
        """
        Copy a folder, including all child items and child folders.

        :param srcFolder: the folder to copy.
        :type srcFolder: dict
        :param parent: The parent document.  Must be a folder, user, or
                       collection.
        :type parent: dict
        :param name: The name of the new folder.  None to copy the original
                     name.
        :type name: str
        :param description: Description for the new folder.  None to copy the
                            original description.
        :type description: str
        :param parentType: What type the parent is:
                           ('folder' | 'user' | 'collection')
        :type parentType: str
        :param public: Public read access flag.  None to inherit from parent,
                       'original' to inherit from original folder.
        :type public: bool, None, or 'original'.
        :param creator: user representing the creator of the new folder.
        :type creator: dict
        :param progress: a progress context to record process on.
        :type progress: girderformindlogger.utility.progress.ProgressContext or None.
        :param firstFolder: if not None, the first folder copied in a tree of
                            folders.
        :returns: the new folder document.
        """
        setResponseTimeLimit()
        if parentType is None:
            parentType = srcFolder['parentCollection']
        parentType = parentType.lower()
        if parentType not in ('folder', 'user', 'collection'):
            raise ValidationException('The parentType must be folder, '
                                      'collection, or user.')
        if parent is None:
            parent = ModelImporter.model(parentType).load(srcFolder['parentId'], force=True)
        if name is None:
            name = srcFolder['name']
        if description is None:
            description = srcFolder['description']
        if public is not None and isinstance(public, six.string_types):
            if public == 'original':
                public = srcFolder.get('public', None)
            else:
                public = public == 'true'
        newFolder = self.createFolder(
            parentType=parentType, parent=parent, name=name,
            description=description, public=public, creator=creator,
            allowRename=True)
        if firstFolder is None:
            firstFolder = newFolder
        return self.copyFolderComponents(
            srcFolder, newFolder, creator, progress, firstFolder)
Esempio n. 25
0
 def _validateUploadMinimumChunkSize(doc):
     try:
         doc['value'] = int(doc['value'])
         if doc['value'] >= 0:
             return
     except ValueError:
         pass  # We want to raise the ValidationException
     raise ValidationException(
         'Upload minimum chunk size must be an integer >= 0.', 'value')
Esempio n. 26
0
 def _validateCorsAllowMethods(doc):
     if isinstance(doc['value'], six.string_types):
         methods = doc['value'].replace(',', ' ').strip().upper().split()
         # remove duplicates
         methods = list(OrderedDict.fromkeys(methods))
         doc['value'] = ', '.join(methods)
         return
     raise ValidationException(
         'Allowed methods must be a comma-separated list or an empty string.',
         'value')
Esempio n. 27
0
    def validate(self, doc):
        doc['name'] = doc['name'].strip()
        doc['lowerName'] = doc['name'].lower()
        doc['description'] = doc['description'].strip()

        if not doc['name']:
            raise ValidationException('Group name must not be empty.', 'name')

        q = {
            'lowerName': doc['lowerName'],
        }
        if '_id' in doc:
            q['_id'] = {'$ne': doc['_id']}
        duplicate = self.findOne(q, fields=['_id'])
        if duplicate is not None:
            raise ValidationException('A group with that name already exists.',
                                      field='name')

        return doc
Esempio n. 28
0
    def createScreen(self,
                     name,
                     creator,
                     activity=None,
                     description='',
                     readOnly=False):
        """
        Create a new screen.

        :param name: The name of the item.
        :type name: str
        :param description: Description for the screen.
        :type description: str
        :param activity: The parent activity of the screen.
        :param creator: User document representing the creator of the screen.
        :type creator: dict
        :param readOnly: A ready-to-use screen
        :type readOnly: bool
        :returns: The screen item document that was created.
        """
        try:
            activity = ActivityModel().load(activity.get('_id', activity),
                                            level=AccessType.WRITE,
                                            user=creator)
        except:
            raise ValidationException(
                'Invalid Activity ID or inadequate access rights', 'activity')

        now = datetime.datetime.utcnow()

        if not isinstance(creator, dict) or '_id' not in creator:
            # Internal error -- this shouldn't be called without a user.
            raise GirderException(
                'Creator must be a user.',
                'girderformindlogger.models.item.creator-not-user')

        if 'baseParentType' not in activity:
            pathFromRoot = self.parentsToRoot({'folderId': activity['_id']},
                                              creator,
                                              force=True)
            activity['baseParentType'] = pathFromRoot[0]['type']
            activity['baseParentId'] = pathFromRoot[0]['object']['_id']

        return self.save({
            'name': self._validateString(name),
            'description': self._validateString(description),
            'folderId': ObjectId(activity['_id']),
            'creatorId': creator['_id'],
            'baseParentType': activity['baseParentType'],
            'baseParentId': activity['baseParentId'],
            'created': now,
            'updated': now,
            'size': 0,
            'readOnly': readOnly
        })
Esempio n. 29
0
def loadJSON(url, urlType='protocol'):
    from girderformindlogger.exceptions import ValidationException

    print("Loading {} from {}".format(urlType, url))
    try:
        r = requests.get(url)
        data = json5.loads(r.text)
    except:
        return ({})
        raise ValidationException('Invalid ' + urlType + ' URL: ' + url, 'url')
    return (data)
Esempio n. 30
0
def listFromString(string):
    if type(string) not in (str, list):
        if string is None:
            return ([])
        raise ValidationException('Not a string or list.', str(string))
    elif type(string) == list:
        return (string)
    elif string.startswith('['):
        return (literal_eval(string))
    else:
        return ([string])