示例#1
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')
示例#2
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
示例#3
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
示例#4
0
    def login(self):
        import threading
        from girderformindlogger.utility.mail_utils import validateEmailAddress

        if not Setting().get(SettingKey.ENABLE_PASSWORD_LOGIN):
            raise RestException('Password login is disabled on this instance.')

        user, token = self.getCurrentUser(returnToken=True)

        # Only create and send new cookie if user isn't already sending a valid
        # one.
        if not user:
            authHeader = cherrypy.request.headers.get('Authorization')

            if not authHeader:
                authHeader = cherrypy.request.headers.get(
                    'Girder-Authorization')

            if not authHeader or not authHeader[0:6] == 'Basic ':
                raise RestException('Use HTTP Basic Authentication', 401)

            try:
                credentials = base64.b64decode(authHeader[6:]).decode('utf8')
                if ':' not in credentials:
                    raise TypeError
            except Exception:
                raise RestException('Invalid HTTP Authorization header', 401)

            login, password = credentials.split(':', 1)
            if validateEmailAddress(login):
                raise AccessException(
                    "Please log in with a username, not an email address.")
            otpToken = cherrypy.request.headers.get('Girder-OTP')
            try:
                user = self._model.authenticate(login, password, otpToken)
            except:
                raise AccessException(
                    "Incorrect password for {} if that user exists".format(
                        login))

            thread = threading.Thread(
                target=AppletModel().updateUserCacheAllRoles, args=(user, ))

            setCurrentUser(user)
            token = self.sendAuthTokenCookie(user)

        return {
            'user': self._model.filter(user, user),
            'authToken': {
                'token': token['_id'],
                'expires': token['expires'],
                'scope': token['scope']
            },
            'message': 'Login succeeded.'
        }
示例#5
0
    def createAppletFromUrl(self,
                            name,
                            protocolUrl,
                            user=None,
                            roles=None,
                            constraints=None,
                            email='',
                            sendEmail=True):
        from girderformindlogger.models.protocol import Protocol
        from girderformindlogger.utility import mail_utils

        # we have cases to show manager's email to users
        if mail_utils.validateEmailAddress(email):
            user['email'] = email
            user['email_encrypted'] = False
            UserModel().save(user)

        # get a protocol from a URL
        protocol = Protocol().getFromUrl(protocolUrl,
                                         'protocol',
                                         user,
                                         thread=False,
                                         refreshCache=True)

        protocol = protocol[0].get('protocol', protocol[0])

        displayName = Protocol().preferredName(protocol)

        name = name if name is not None and len(name) else displayName

        appletName = '{}/'.format(protocolUrl)

        applet = self.createApplet(
            name=name,
            protocol={
                '_id':
                'protocol/{}'.format(str(protocol.get('_id')).split('/')[-1]),
                'url':
                protocol.get('meta', {}).get('protocol',
                                             {}).get('url', protocolUrl)
            },
            user=user,
            roles=roles,
            constraints=constraints,
            appletName=appletName)
示例#6
0
    def createAppletFromProtocolData(self,
                                     name,
                                     protocol,
                                     user=None,
                                     roles=None,
                                     constraints=None,
                                     email='',
                                     sendEmail=True):
        from girderformindlogger.models.protocol import Protocol
        from girderformindlogger.utility import mail_utils

        # we have cases to show manager's email to users
        if mail_utils.validateEmailAddress(email):
            user['email'] = email
            user['email_encrypted'] = False
            UserModel().save(user)

        # get a protocol from single json file
        protocol = Protocol().createProtocol(protocol, user)

        protocol = protocol.get('protocol', protocol)

        displayName = Protocol().preferredName(protocol)

        name = name if name is not None and len(name) else displayName

        appletName = '{}/'.format(protocol.get('@id'))

        applet = self.createApplet(
            name=name,
            protocol={
                '_id':
                'protocol/{}'.format(str(protocol.get('_id')).split('/')[-1])
            },
            user=user,
            roles=roles,
            constraints=constraints,
            appletName=appletName)
示例#7
0
def _invite(applet, user, role, rsvp, subject):
    """
    Helper function to invite a user to an applet.

    :param applet: Applet to invite user to
    :type applet: AppletModel
    :param user: ID (canonical or applet-specific) or email address of user to
                 invite
    :type user: string
    :param role: Role to invite user to
    :type role: string
    :param rsvp: Require user acceptance?
    :type rsvp: boolean
    :param subject: Subject about 'user' role can inform or about which
                    'reviewer' role can review
    :type subject: string or literal
    :returns: New assignment (dictionary)
    """
    if role not in USER_ROLE_KEYS:
        raise ValidationException('Invalid role.', 'role')
    thisUser = Applet().getCurrentUser()
    user = user if user else str(thisUser['_id'])

    if mail_utils.validateEmailAddress(user):
        user = UserModel().hash(user)

    if bool(rsvp):
        groupName = {'title': '{} {}s'.format(str(applet.get('_id')), role)}
        groupName['lower'] = groupName.get('title', '').lower()
        group = GroupModel().findOne(query={'lowerName': groupName['lower']})
        if not group or group is None:
            group = GroupModel().createGroup(
                name=groupName['title'],
                creator=thisUser,
                public=bool(role in ['manager', 'reviewer']))
    try:
        assignments = CollectionModel().createCollection(name="Assignments",
                                                         public=True,
                                                         reuseExisting=True)
        assignmentType = 'collection'
    except AccessException:
        assignments, assignmentType = selfAssignment()
    appletAssignment = list(FolderModel().childFolders(
        parent=assignments,
        parentType=assignmentType,
        user=thisUser,
        filters={
            'meta.applet.@id': str(applet['_id']) if '_id' in applet else None
        }))
    appletAssignment = appletAssignment[0] if len(
        appletAssignment) else FolderModel().setMetadata(
            FolderModel().createFolder(
                parent=assignments,
                name=FolderModel().preferredName(applet),
                parentType=assignmentType,
                public=False,
                creator=thisUser,
                allowRename=True,
                reuseExisting=False), {
                    'applet': {
                        '@id': str(applet['_id']) if '_id' in applet else None
                    }
                })
    meta = appletAssignment.get('meta', {})
    members = meta.get('members',
                       []) if meta.get('members') is not None else []
    cUser = getUserCipher(appletAssignment, user)
    subject = subject.upper() if subject is not None and subject.upper(
    ) in SPECIAL_SUBJECTS else getUserCipher(
        appletAssignment,
        str(thisUser['_id']) if subject is None else subject)
    thisAppletAssignment = {
        '@id': str(cUser),
        'roles': {
            role: True if role not in ['reviewer', 'user'] else [subject]
        }
    }
    for i, u in enumerate(members):
        if '@id' in u and u["@id"] == str(cUser):
            thisAppletAssignment = members.pop(i)
            if 'roles' not in thisAppletAssignment:
                thisAppletAssignment['roles'] = {}
            thisAppletAssignment['roles'][role] = True if role not in [
                'reviewer', 'user'
            ] else [subject] if (subject in SPECIAL_SUBJECTS) or (
                'reviewer' not in thisAppletAssignment['roles']) else list(
                    set(thisAppletAssignment['roles']['reviewer'] +
                        [subject]).difference(set(SPECIAL_SUBJECTS))
                ) if "ALL" not in thisAppletAssignment['roles'][
                    'reviewer'] else ["ALL"]
    members.append(thisAppletAssignment)
    meta['members'] = members
    appletAssignment = FolderModel().setMetadata(appletAssignment, meta)
    authorizeReviewers(appletAssignment)
    return (appletAssignment)
示例#8
0
    def inviteUser(self,
                   applet,
                   role="user",
                   email='',
                   firstName='',
                   lastName='',
                   MRN=''):
        from girderformindlogger.models.invitation import Invitation
        from girderformindlogger.models.profile import Profile

        if not mail_utils.validateEmailAddress(email):
            raise ValidationException('invalid email', 'email')

        thisUser = self.getCurrentUser()

        encryptedEmail = UserModel().hash(email)
        invitedUser = UserModel().findOne({
            'email': encryptedEmail,
            'email_encrypted': True
        })

        if not invitedUser:
            invitedUser = UserModel().findOne({
                'email': email,
                'email_encrypted': {
                    '$ne': True
                }
            })

        if not AppletModel().isCoordinator(applet['_id'], thisUser):
            raise AccessException(
                "Only coordinators and managers can invite users.")

        if role not in USER_ROLE_KEYS:
            raise ValidationException('Invalid role.', 'role')

        invitation = Invitation().createInvitationForSpecifiedUser(
            applet=applet,
            coordinator=thisUser,
            role=role,
            user=invitedUser,
            firstName=firstName,
            lastName=lastName,
            MRN=MRN,
            userEmail=encryptedEmail)

        url = 'web.mindlogger.org/#/invitation/%s' % (str(invitation['_id'], ))

        managers = mail_utils.htmlUserList(AppletModel().listUsers(applet,
                                                                   'manager',
                                                                   force=True))
        coordinators = mail_utils.htmlUserList(AppletModel().listUsers(
            applet, 'coordinator', force=True))
        reviewers = mail_utils.htmlUserList(AppletModel().listUsers(
            applet, 'reviewer', force=True))

        html = mail_utils.renderTemplate(
            'userInvite.mako'
            if invitedUser else 'inviteUserWithoutAccount.mako', {
                'url': url,
                'userName': firstName,
                'coordinatorName': thisUser['firstName'],
                'appletName': applet['displayName'],
                'MRN': MRN,
                'managers': managers,
                'coordinators': coordinators,
                'reviewers': reviewers
            })

        mail_utils.sendMail('invitation for an applet', html, [email])

        return 'sent invitation mail to {}'.format(email)
    def acceptInvitation(self, invitation, user, userEmail = ''): # we need to save coordinator/manager's email as plain text
        from girderformindlogger.models.applet import Applet
        from girderformindlogger.models.ID_code import IDCode
        from girderformindlogger.models.profile import Profile
        from girderformindlogger.utility import mail_utils

        applet = Applet().load(invitation['appletId'], force=True)
        profiles = None
        if 'idCode' in invitation:
            profiles = IDCode().findProfile(invitation['idCode'])
        if profiles and len(profiles):
            profile = [
                pro for pro in profiles if str(
                    pro.get('userId')
                )==str(user['_id'])
            ]
            profile = profile[0] if len(profile) else None
        else:
            profile = None
            Profile().removeWithQuery({ '_id': ObjectId(invitation['_id']) })

        if profile==None or not len(profile):
            profile = Profile().createProfile(
                applet,
                user,
                role=invitation.get('role', 'user')
            )
            IDCode().createIdCode(profile, invitation.get('idCode'))
        if 'schema:knows' in invitation:
            if 'schema:knows' not in profile:
                profile['schema:knows'] = invitation['schema:knows']
            else:
                for k in invitation['schema:knows']:
                    if k in profile['schema:knows']:
                        profile['schema:knows'][k].extend([
                            r for r in invitation['schema:knows'][
                                k
                            ] if r not in profile['schema:knows'][k]
                        ])
                    else:
                        profile['schema:knows'][k] = invitation['schema:knows'][
                            k
                        ]

        # append role value
        profile = Profile().load(profile['_id'], force=True)
        profile['roles'] = profile.get('roles', [])
        invited_role = invitation.get('role','user')

        new_roles = []
        # manager has get all roles by default
        for role in USER_ROLES.keys():
            if role not in profile['roles']:
                if invited_role == 'manager' or invited_role == role or role == 'user':
                    new_roles.append(role)
                    profile['roles'].append(role)

        profile['firstName'] = invitation.get('firstName', '')
        profile['lastName'] = invitation.get('lastName', '')
        profile['MRN'] = invitation.get('MRN', '')

        Profile().save(profile, validate=False)

        from girderformindlogger.models.user import User as UserModel
        
        if not mail_utils.validateEmailAddress(userEmail):
            raise ValidationException(
                'Invalid email address.',
                'email'
            )
        if invited_role != 'user' and user.get('email_encrypted', False):
            if UserModel().hash(userEmail) != user['email']:
                raise ValidationException(
                    'Invalid email address.',
                    'email'
                )
            user['email'] = userEmail
            user['email_encrypted'] = False

            UserModel().save(user)

        UserModel().appendApplet(user, applet['_id'], new_roles)

        self.remove(invitation)        
        return(Profile().displayProfileFields(
            Profile().load(profile['_id'], force=True),
            user
        ))
示例#10
0
    def createUser(self,
                   login,
                   password,
                   displayName="",
                   email="",
                   admin=False,
                   public=False,
                   currentUser=None,
                   firstName="",
                   lastName="",
                   encryptEmail=False):
        """
        Create a new user with the given information.

        :param admin: Whether user is global administrator.
        :type admin: bool
        :param public: Whether user is publicly visible.
        :type public: bool
        :returns: The user document that was created.
        """
        from girderformindlogger.models.group import Group
        from girderformindlogger.models.setting import Setting
        requireApproval = Setting().get(
            SettingKey.REGISTRATION_POLICY) == 'approve'
        email = "" if not email else email

        login = login.lower().strip()
        email = email.lower().strip()

        if self.findOne({
                'email': email,
                'email_encrypted': {
                    '$ne': True
                }
        }) or self.findOne({
                'email': self.hash(email),
                'email_encrypted': True
        }):
            raise ValidationException(
                'That email is already registered in the system.', )

        if admin:
            requireApproval = False
            encryptEmail = False
        user = {
            'login':
            login,
            'email':
            email,
            'displayName':
            displayName if len(displayName) else
            firstName if firstName is not None else "",
            'firstName':
            firstName,
            'lastName':
            lastName,
            'created':
            datetime.datetime.utcnow(),
            'emailVerified':
            False,
            'status':
            'pending' if requireApproval else 'enabled',
            'admin':
            admin,
            'size':
            0,
            'deviceId':
            '',
            'timezone':
            0,
            'groups': [],
            'groupInvites': [{
                "groupId": gi.get('_id'),
                "level": 0
            } for gi in list(Group().find(
                query={"queue": email}))] if len(email) else [],
            'email_encrypted':
            encryptEmail
        }
        if encryptEmail:
            if len(email) == 0 or not mail_utils.validateEmailAddress(email):
                raise ValidationException('Invalid email address.', 'email')

            user['email'] = self.hash(user['email'])

        self.setPassword(user, password, save=False)
        self.setPublic(user, public, save=False)

        if currentUser:
            self.setUserAccess(user,
                               user=currentUser,
                               level=AccessType.WRITE,
                               save=False)
            user['creatorId'] = currentUser['_id']

        user = self.save(user)

        if currentUser:
            User().setUserAccess(doc=currentUser,
                                 user=user,
                                 level=AccessType.READ,
                                 save=True)
        else:
            user['creatorId'] = user['_id']
            user = self.save(user)

        verifyEmail = Setting().get(
            SettingKey.EMAIL_VERIFICATION) != 'disabled'
        if verifyEmail:
            self._sendVerificationEmail(user, email)

        if requireApproval:
            self._sendApprovalEmail(user)
        Group().update(query={"queue": user['email']},
                       update={"$pull": {
                           "queue": user['email']
                       }},
                       multi=True)
        user = self._getGroupInvitesFromProtoUser(user)
        self._deleteProtoUser(user)
        return (user)
示例#11
0
    def login(self, loginAsEmail):
        import threading
        from girderformindlogger.utility.mail_utils import validateEmailAddress

        if not Setting().get(SettingKey.ENABLE_PASSWORD_LOGIN):
            raise RestException('Password login is disabled on this instance.')

        user, token = self.getCurrentUser(returnToken=True)

        deviceId = cherrypy.request.headers.get('deviceId', '')
        timezone = int(cherrypy.request.headers.get('timezone', 0))

        # Only create and send new cookie if user isn't already sending a valid
        # one.
        if not user:
            authHeader = cherrypy.request.headers.get('Authorization')

            if not authHeader:
                authHeader = cherrypy.request.headers.get(
                    'Girder-Authorization'
                )

            if not authHeader or not authHeader[0:6] == 'Basic ':
                raise RestException('Use HTTP Basic Authentication', 401)

            try:
                credentials = base64.b64decode(authHeader[6:]).decode('utf8')
                if ':' not in credentials:
                    raise TypeError
            except Exception:
                raise RestException('Invalid HTTP Authorization header', 401)

            login, password = credentials.split(':', 1)

            isEmail = validateEmailAddress(login)

            if not loginAsEmail and isEmail:
                raise AccessException(
                    "Please log in with a username, not an email address."
                )
            if loginAsEmail and not isEmail:
                raise AccessException(
                    "Please enter valid email address"
                )

            otpToken = cherrypy.request.headers.get('Girder-OTP')
            try:
                user = self._model.authenticate(login, password, otpToken, loginAsEmail = True)
            except:
                raise AccessException(
                    "Incorrect password for {} if that user exists".format(
                        login
                    )
                )
            if user.get('exception', None):
                raise AccessException(
                    user['exception']
                )

            if deviceId:
                user['deviceId'] = deviceId
                user['timezone'] = timezone
                self._model.save(user)

            setCurrentUser(user)
            token = self.sendAuthTokenCookie(user)

        return {
            'user': self._model.filter(user, user),
            'authToken': {
                'token': token['_id'],
                'expires': token['expires'],
                'scope': token['scope']
            },
            'message': 'Login succeeded.'
        }