def checkTemporaryPassword(self, user, token): token = Token().load( token, user=user, level=AccessType.ADMIN, objectId=False, exc=True) delta = (token['expires'] - datetime.datetime.utcnow()).total_seconds() hasScope = Token().hasScope(token, TokenScope.TEMPORARY_USER_AUTH) if delta <= 0: raise AccessException("The token is expired") if token.get('userId') != user['_id'] or not hasScope: raise AccessException('The token does not grant temporary access to this user.') # Temp auth is verified, send an actual auth token now. We keep the # temp token around since it can still be used on a subsequent request # to change the password authToken = self.sendAuthTokenCookie(user) return { 'user': self._model.filter(user, user), 'authToken': { 'token': authToken['_id'], 'expires': authToken['expires'], 'temporary': True }, 'message': 'Temporary access token is valid.' }
def updateUser( self, user, displayName="", email="", admin=False, status=None, firstName=None, lastName=None ): # 🔥 delete firstName and lastName once fully deprecated user['firstName'] = displayName if len( displayName) else firstName if firstName is not None else "" user['email'] = email # Only admins can change admin state if admin is not None: if self.getCurrentUser()['admin']: user['admin'] = admin elif user['admin'] is not admin: raise AccessException('Only admins may change admin status.') # Only admins can change status if status is not None and status != user.get('status', 'enabled'): if not self.getCurrentUser()['admin']: raise AccessException('Only admins may change status.') if user['status'] == 'pending' and status == 'enabled': # Send email on the 'pending' -> 'enabled' transition self._model._sendApprovedEmail(user) user['status'] = status return self._model.save(user)
def setSchedule(self, applet, schedule, **kwargs): import threading thisUser = self.getCurrentUser() if not AppletModel()._hasRole(applet['_id'], thisUser, 'user'): raise AccessException( "You aren't a user of this applet." ) profile = ProfileModel().findOne( { 'appletId': applet['_id'], 'userId': thisUser['_id'], 'profile': True } ) if not profile: raise AccessException( "You aren't a user of this applet." ) ud = profile["userDefined"] if "userDefined" in profile else {} ud["schedule"] = schedule profile["userDefined"] = ud ProfileModel().save(profile, validate=False) return(profile["userDefined"])
def setOtherSchedule(self, profile, applet, schedule, **kwargs): import threading thisUser = self.getCurrentUser() if not AppletModel().isCoordinator(applet['_id'], thisUser): raise AccessException( "You aren't a coordinator or manager of this applet." ) if profile["appletId"] not in [applet['_id'], str(applet['_id'])]: raise AccessException( "That profile is not a user of this applet." ) ud = profile[ "coordinatorDefined" ] if "coordinatorDefined" in profile else {} ud["schedule"] = schedule profile["coordinatorDefined"] = ud ProfileModel().save(profile, validate=False) thread = threading.Thread( target=AppletModel().updateUserCacheAllUsersAllRoles, args=(applet, thisUser) ) thread.start() return(profile["coordinatorDefined"])
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.' }
def getAppletUsers(self, applet): thisUser = self.getCurrentUser() if AppletModel().isCoordinator(applet['_id'], thisUser): return (AppletModel().getAppletUsers(applet, thisUser, force=True)) else: raise AccessException( "Only coordinators and managers can see user lists.")
def ensureTokenScopes(token, scope): """ Call this to validate a token scope for endpoints that require tokens other than a user authentication token. Raises an AccessException if the required scopes are not allowed by the given token. :param token: The token object used in the request. :type token: dict :param scope: The required scope or set of scopes. :type scope: `str or list of str` """ tokenModel = Token() if tokenModel.hasScope(token, TokenScope.USER_AUTH): return if not tokenModel.hasScope(token, scope): setCurrentUser(None) if isinstance(scope, six.string_types): scope = (scope,) raise AccessException( 'Invalid token scope.\n' 'Required: %s.\n' 'Allowed: %s' % ( ' '.join(scope), '' if token is None else ' '.join(tokenModel.getAllowedScopes(token))))
def wrapped(*args, **kwargs): if not rest.getCurrentToken(): raise AccessException( 'You must be logged in or have a valid auth token.') if required: Token().requireScope(rest.getCurrentToken(), scope) return fun(*args, **kwargs)
def removeFromGroup(self, group, userToRemove, delete): user = self.getCurrentUser() if not (bool( group.get('_id') in [ *user.get('groups', []), *user.get('formerGroups', []), *[ g.get('groupId') for g in [ *user.get('groupInvites', []), *user.get('declinedInvites', []) ] ] ] )): raise AccessException(message="You haven't been invited to that group.") else: groupModel = self._model if userToRemove is None: # Assume user is removing themself from the group userToRemove = user # # If removing someone else, you must have at least as high an # # access level as they do, and you must have at least write access # # to remove any user other than yourself. # if user['_id'] != userToRemove['_id']: # if groupModel.hasAccess(group, userToRemove, AccessType.ADMIN): # groupModel.requireAccess(group, user, AccessType.ADMIN) # else: # groupModel.requireAccess(group, user, AccessType.WRITE) group = groupModel.removeUser(group, userToRemove, delete) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return(group)
def acceptInvitationByToken(self, invitation, lang, email, token): """ Accept an invitation. """ currentUser = Token().load( token, force=True, objectId=False, exc=False ).get('userId', None) if currentUser is not None: currentUser = UserModel().load(currentUser, force=True) if currentUser is None: raise AccessException( "Invalid token." ) if invitation.get('role', 'user') == 'owner': AppletModel().receiveOwnerShip(AppletModel().load(invitation['appletId'], force=True), currentUser, email) else: profile = InvitationModel().acceptInvitation(invitation, currentUser, email) # editors should be able to access duplicated applets if invitation.get('role','user') == 'editor' or invitation.get('role', 'user') == 'manager': InvitationModel().accessToDuplicatedApplets(invitation, currentUser, email) InvitationModel().remove(invitation) return profile
def verifyEmail(self, user, token): token = Token().load(token, user=user, level=AccessType.ADMIN, objectId=False, exc=True) delta = (token['expires'] - datetime.datetime.utcnow()).total_seconds() hasScope = Token().hasScope(token, TokenScope.EMAIL_VERIFICATION) if token.get('userId') != user['_id'] or delta <= 0 or not hasScope: raise AccessException('The token is invalid or expired.') user['emailVerified'] = True Token().remove(token) user = self._model.save(user) if self._model.canLogin(user): setCurrentUser(user) authToken = self.sendAuthTokenCookie(user) return { 'user': self._model.filter(user, user), 'authToken': { 'token': authToken['_id'], 'expires': authToken['expires'], 'scope': authToken['scope'] }, 'message': 'Email verification succeeded.' } else: return { 'user': self._model.filter(user, user), 'message': 'Email verification succeeded.' }
def createCollection(self, name, description, public): user = self.getCurrentUser() if not self._model.hasCreatePrivilege(user): raise AccessException('You are not authorized to create collections.') return self._model.createCollection( name=name, description=description, public=public, creator=user)
def cancelUpload(self, upload): user = self.getCurrentUser() if upload['userId'] != user['_id'] and not user['admin']: raise AccessException('You did not initiate this upload.') Upload().cancelUpload(upload) return {'message': 'Upload canceled.'}
def getUserApplets(self, user, role, ids_only): from bson.objectid import ObjectId reviewer = self.getCurrentUser() if reviewer is None: raise AccessException("You must be logged in to get user applets.") if user.get('_id') != reviewer.get('_id') and user.get( '_id') is not None: raise AccessException("You can only get your own applets.") role = role.lower() if role not in USER_ROLES.keys(): raise RestException('Invalid user role.', 'role') try: applets = AppletModel().getAppletsForUser(role, user, active=True) if len(applets) == 0: return ([]) if ids_only == True: return ([applet.get('_id') for applet in applets]) return ([{ **jsonld_expander.formatLdObject(applet, 'applet', reviewer, refreshCache=False), "users": AppletModel().getAppletUsers(applet, user), "groups": AppletModel().getAppletGroups(applet, arrayOfObjects=True) } if role == "manager" else { **jsonld_expander.formatLdObject(applet, 'applet', reviewer, dropErrors=True), "groups": [ group for group in AppletModel().getAppletGroups( applet).get(role) if ObjectId(group) in [ *user.get('groups', []), *user.get('formerGroups', []), *[ invite['groupId'] for invite in [ *user.get('groupInvites', []), *user.get('declinedInvites', []) ] ] ] ] } for applet in applets if (applet is not None and not applet.get( 'meta', {}).get('applet', {}).get('deleted'))]) except Exception as e: return (e)
def setUserRelationship(self, id, rel, otherId, otherName): from girderformindlogger.models.invitation import Invitation from girderformindlogger.utility.jsonld_expander import \ inferRelationships, oidIffHex user = self.getCurrentUser() grammaticalSubject = ProfileModel().getProfile(id, user) gsp = ProfileModel().load( grammaticalSubject['_id'], force=True ) grammaticalSubject = Invitation().load( grammaticalSubject['_id'], force=True ) if gsp is None else gsp print(grammaticalSubject) if grammaticalSubject is None or not AppletModel().isCoordinator( grammaticalSubject['appletId'], user ): raise AccessException( 'You do not have permission to update this user.' ) appletId = grammaticalSubject['appletId'] grammaticalObject = ProfileModel().getSubjectProfile( otherId, otherName, user ) if grammaticalObject is None: grammaticalObject = ProfileModel().getProfile( ProfileModel().createPassiveProfile( appletId, otherId, otherName, user )['_id'], grammaticalSubject ) if 'schema:knows' in grammaticalSubject: if rel in grammaticalSubject['schema:knows'] and grammaticalObject[ '_id' ] not in grammaticalSubject['schema:knows'][rel]: grammaticalSubject['schema:knows'][rel].append( grammaticalObject['_id'] ) else: grammaticalSubject['schema:knows'][rel] = [ grammaticalObject['_id'] ] else: grammaticalSubject['schema:knows'] = { rel: [grammaticalObject['_id']] } ProfileModel().save(grammaticalSubject, validate=False) inferRelationships(grammaticalSubject) return(ProfileModel().getProfile(id, user))
def acceptInvitation(self, invitation): """ Accept an invitation. """ currentUser = self.getCurrentUser() if currentUser is None: raise AccessException( "You must be logged in to accept an invitation.") return (InvitationModel().acceptInvitation(invitation, currentUser))
def updateInformant(self, applet, informant): user = self.getCurrentUser() if not AppletModel().isManager(applet['_id'], user): raise AccessException( "Only managers can update informant relationship") AppletModel().updateRelationship(applet, informant) return (jsonld_expander.formatLdObject(applet, 'applet', user, refreshCache=False))
def updateUser( self, user, displayName="", email="", admin=False, status=None, firstName=None, lastName=None ): user['displayName'] = displayName if len( displayName ) else firstName if firstName is not None else "" user['email'] = UserModel().hash(email) user['email_encrypted'] = True # Only admins can change admin state if admin is not None: if self.getCurrentUser()['admin']: user['admin'] = admin elif user['admin'] is not admin: raise AccessException('Only admins may change admin status.') # Only admins can change status if status is not None and status != user.get('status', 'enabled'): if not self.getCurrentUser()['admin']: raise AccessException('Only admins may change status.') if user['status'] == 'pending' and status == 'enabled': # Send email on the 'pending' -> 'enabled' transition self._model._sendApprovedEmail(user, email) user['status'] = status try: self._model.save(user) except: raise RestException( 'Update failed, and `PUT /user/{:id}` is deprecated.' ) return( {'message': 'Update saved, but `PUT /user/{:id}` is deprecated.'} )
def requireScope(self, token, scope): """ Raise an error if given set of scopes are not included. :param token: The token object. :type token: dict :param scope: A scope or set of scopes that will be tested as a subset of the given token's allowed scopes. :type scope: str or list of str """ if not self.hasScope(token, scope): raise AccessException('Invalid token scope, required: %s.' % (scope))
def requireAdmin(user, message=None): """ Calling this on a user will ensure that they have admin rights. If not, raises an AccessException. :param user: The user to check admin flag on. :type user: dict. :param message: The exception message. :type message: str or None :raises AccessException: If the user is not an administrator. """ if user is None or not user['admin']: raise AccessException(message or 'Administrator access required.')
def removeIDCode(self, id, code): from bson.objectid import ObjectId user = self.getCurrentUser() try: p = ProfileModel().findOne({'_id': ObjectId(id)}) except: p = None if p is None or not AppletModel().isCoordinator(p['appletId'], user): raise AccessException( 'You do not have permission to update this user\'s ID code.') else: IDCode().removeCode(p['_id'], code) return (ProfileModel().profileAsUser( ProfileModel().load(p['_id'], force=True), user))
def acceptInvitationByToken(self, invitation, email, token): """ Accept an invitation. """ currentUser = Token().load(token, force=True, objectId=False, exc=False).get('userId', None) if currentUser is not None: currentUser = UserModel().load(currentUser, force=True) if currentUser is None: raise AccessException("Invalid token.") return (InvitationModel().acceptInvitation(invitation, currentUser, email))
def getSchedule(self, applet, getAllEvents=False, refreshCache=False): user = self.getCurrentUser() if not getAllEvents: schedule = EventsModel().getScheduleForUser( applet['_id'], user['_id'], AppletModel().isCoordinator(applet['_id'], user)) else: if not AppletModel().isCoordinator(applet['_id'], user): raise AccessException( "Only coordinators and managers can get all events.") schedule = EventsModel().getSchedule(applet['_id']) return schedule
def acceptInvitationByToken(self, invitation, token): """ Accept an invitation. """ currentUser = Token().load(token, force=True, objectId=False, exc=False).get('userId') if currentUser is not None: currentUser = UserModel().load(currentUser, force=True) if currentUser is None: raise AccessException( "You must be logged in to accept an invitation.") return (InvitationModel().acceptInvitation(invitation, currentUser))
def setSchedule(self, applet, schedule, **kwargs): thisUser = self.getCurrentUser() if not AppletModel().isCoordinator(applet['_id'], thisUser): raise AccessException( "Only coordinators and managers can update applet schedules.") appletMeta = applet['meta'] if 'meta' in applet else {'applet': {}} if 'applet' not in appletMeta: appletMeta['applet'] = {} appletMeta['applet']['schedule'] = schedule AppletModel().setMetadata(applet, appletMeta) thread = threading.Thread( target=AppletModel().updateUserCacheAllUsersAllRoles, args=(applet, thisUser)) thread.start() return (appletMeta)
def __enter__(self): try: if self.encryption == 'ssl': self.connection = smtplib.SMTP_SSL(self.host, self.port) else: self.connection = smtplib.SMTP(self.host, self.port) if self.encryption == 'starttls': self.connection.starttls() if self.username and self.password: self.connection.login(self.username, self.password) except: raise AccessException( "An error occured when we were sending message. " "Please try again later.") return self
def setSchedule(self, applet, schedule, **kwargs): thisUser = self.getCurrentUser() if not AppletModel().isCoordinator(applet['_id'], thisUser): raise AccessException( "Only coordinators and managers can update applet schedules.") if 'events' in schedule: for event in schedule['events']: if 'data' in event and 'useNotifications' in event[ 'data'] and event['data']['useNotifications']: if event['data']['notifications'][0]['start']: sendTime = event['data']['notifications'][0]['start'] else: sendTime = '09:00' # in case of sigle event with exact year, month, day if 'year' in event['schedule'] and 'month' in event[ 'schedule'] and 'dayOfMonth' in event['schedule']: sendTime = ( str(event['schedule']['year'][0]) + '/' + ('0' + str(event['schedule']['month'][0] + 1))[-2:] + '/' + ('0' + str(event['schedule']['dayOfMonth'][0]))[-2:] + ' ' + sendTime) existNotification = PushNotificationModel().findOne( query={ 'applet': applet['_id'], 'creator_id': thisUser['_id'], 'sendTime': str(sendTime) }) if not existNotification: PushNotificationModel().createNotification( applet['_id'], 1, event['data']['title'], event['data']['description'], str(sendTime), thisUser['_id']) # in case of daily event appletMeta = applet['meta'] if 'meta' in applet else {'applet': {}} if 'applet' not in appletMeta: appletMeta['applet'] = {} appletMeta['applet']['schedule'] = schedule AppletModel().setMetadata(applet, appletMeta) thread = threading.Thread( target=AppletModel().updateUserCacheAllUsersAllRoles, args=(applet, thisUser)) thread.start() return (appletMeta)
def _promote(self, group, user, level): """ Promote a user to moderator or administrator. :param group: The group to promote within. :param user: The user to promote. :param level: Either WRITE or ADMIN, for moderator or administrator. :type level: AccessType :returns: The updated group document. """ if not group['_id'] in user.get('groups', []): raise AccessException('That user is not a group member.') group = self._model.setUserAccess(group, user, level=level, save=True) group['access'] = self._model.getFullAccessList(group) return group
def updateProfile(self, profileId, user, profileUpdate): from copy import deepcopy from girderformindlogger.models.applet import Applet profile = self.load(profileId, force=True) if str(user["_id"] == profile["userId"]): update = deepcopy(profile.get("userDefined", {})) update.update(profileUpdate) profile["userDefined"] = update elif Applet().isCoordinator(profile["appletId"], user): update = deepcopy(profile.get("coordinatorDefined", {})) update.update(profileUpdate) profile["coordinatorDefined"] = update else: raise AccessException( "You do not have adequate permissions to update this profile.") return self.save(profile, validate=False)
def refresh(self, applet): user = self.getCurrentUser() if not AppletModel().isCoordinator(applet['_id'], user): raise AccessException( "Only coordinators and managers can update applet.") thread = threading.Thread(target=AppletModel().reloadAndUpdateCache, args=(applet, user)) thread.start() return ({ "message": "The protocol is being reloaded and cached data is being updated. Please check back " "in several mintutes to see it." })