def protected_get_user_by_username(cls, username): """ Retreives the User object for the given username, provided the username is that of the currently logged in user, or the currently logged in user is a mod. If the username is not that of the currently logged in user, and the currently logged in user is not a mod, then an exception is raised. :param username: The username of the user to get :returns: The appropriate User object """ if not cls.valid_username(username): raise BadRequestException('No or invalid username provided') current_user = cls.get_current_user(verified_email_required=False) if current_user is None: raise UnauthorizedException('User not logged in') elif current_user.username_lower == username.lower(): return current_user elif current_user.is_mod: user = User.get_by_username(username) if user is None: raise BadRequestException('No user exists for username ' + username) return user else: raise ForbiddenException('Insufficient privilages')
def PasswordReset(self, prm): user = User.get_by_email(prm.email) if user is None: raise BadRequestException("No user registered with that email") if not prm.set_password_url: raise BadRequestException("No set password url provided") # Send password reset email ok, msg = user.send_password_reset_email(prm.set_password_url) if not ok: raise ConflictException(msg) return prm
def UpdateUser(self, spm): user = self.protected_get_user_by_username(spm.old_username) # Make sure new request params are acceptable invalid_params = dict() ok, msg = self.valid_username(spm.username) if not ok: invalid_params['username'] = msg if spm.password is not None and len(spm.password) > 0: if not user.check_password(spm.old_password): invalid_params['old_password'] = '******' ok, msg = self.valid_password(spm.password) if not ok: invalid_params['new_password'] = msg ok, msg = self.valid_email(spm.email) if not ok: invalid_params['email'] = msg if not spm.verification_url: invalid_params['verification_url'] = ( 'A verification url is required') if len(invalid_params.keys()) > 0: raise BadRequestException('|'.join([ key + ':' + invalid_params[key] for key in invalid_params.keys() ])) # Update the user ok, info, spm.verification_email_sent = self.update_user_internal( user, spm.email, spm.username, spm.password, spm.verification_url) if not ok: raise ConflictException(info) return spm
def Register(self, rm): # Validate request params invalid_params = dict() ok, msg = self.valid_username(rm.username) if not ok: invalid_params['username'] = msg ok, msg = self.valid_password(rm.password) if not ok: invalid_params['password'] = msg ok, msg = self.valid_email(rm.email) if not ok: invalid_params['email'] = msg if not rm.verification_url: invalid_params['verification_url'] = ( 'A verification url is required') if len(invalid_params.keys()) > 0: # Some params were invalid. Return 400 response. raise BadRequestException('|'.join([ key + ':' + invalid_params[key] for key in invalid_params.keys() ])) # Create the user with username auth id auth_id = self.get_local_auth_id(rm.username) ok, info = User.create_user(auth_id, password_raw=rm.password, unique_properties=[ 'email_pending', 'username', ], email_pending=rm.email, username=rm.username, is_mod=False) if ok: # Success. Add email auth id so that user can sign in # by email too user = info auth_id = self.get_local_auth_id(user.email_pending) ok, info = user.add_auth_id(auth_id) if not ok: logging.error('Failed to add email auth id [' + auth_id + ' ] to username [' + user.username + ']') # Send email verification user.send_email_verification(rm.verification_url) else: # Failed to create new user. Respond with conflicting properties # separated by a colon, converting auth_id to username info_set = set() for prop in info: if prop == 'auth_id': info_set.add('username') else: info_set.add(prop) raise ConflictException(':'.join(info_set)) return rm
def SendEmailVerification(self, sevm): user = self.protected_get_user_by_username(sevm.username) if not sevm.verification_url: raise BadRequestException("No verification url provided!") # Send email verification if not user.send_email_verification(sevm.verification_url): raise ConflictException('Too many verificaiton emails sent.') sevm.email = user.email_pending return sevm
def Login(self, lm): # Check existence of request params if lm.username_or_email is None or lm.password is None: raise BadRequestException('No username, email or password ' + 'given') # Try to get the User auth_id = self.get_local_auth_id(lm.username_or_email) try: lm.user = User.get_by_auth_password(auth_id, lm.password) except InvalidAuthIdError: raise BadRequestException('Invalid credentials') except InvalidPasswordError: raise BadRequestException('Invalid credentials') user_id = lm.user.get_id() # Log in user with auth token lm.user_id_auth_token = self.create_user_id_auth_token(user_id) if lm.user_id_auth_token is None: raise ConflictException( 'Encountered conflict when creating auth token') return lm
def SetPassword(self, spm): # Check for valid new password ok, msg = self.valid_password(spm.new_password) if not ok: raise BadRequestException(msg) # Check for valid token + grab the user valid = User.validate_password_reset_token(spm.user_id, spm.token) if not valid: raise UnauthorizedException('Invalid token for setting password') user = User.get_by_id(spm.user_id) # Set password ok, info = self.update_user_internal(user, user.email_pending, user.username, spm.new_password) if ok: # Password set successfully User.delete_password_reset_token(spm.user_id, spm.token) else: raise BadRequestException('An unknown error occurred. Please try' + ' again later.') return spm
def VerifyEmail(self, vem): # We require users to be logged in to verify email user = self.get_current_user(verified_email_required=False) if user is None: raise UnauthorizedException('Must be logged in to verify email') # Check for valid token user_id = user.get_id() valid = User.validate_verify_email_token(user_id, vem.token) if not valid: raise BadRequestException('Invalid token for verifying email') self.validate_email_internal(user) User.delete_verify_email_token(user_id, vem.token) return vem
def GetUser(self, user_msg): # First, get the current user current_user = self.get_current_user(verified_email_required=False) if current_user is None: raise UnauthorizedException('Invalid credentials') if current_user.username == user_msg.username: return current_user elif current_user.is_mod: # Validate request params ok, msg = self.valid_username(user_msg.username) if not ok: raise BadRequestException('No or invalid username provided') user = User.get_by_username(user_msg.username) if user is None: raise NotFoundException('No user exists for username ' + user_msg.username) return user else: raise UnauthorizedException('Insufficient privilages')
def SocialLogin(self, slm): if slm.provider is None or slm.access_token is None: raise BadRequestException('No provider or access token given') # Fetch the user info social_id = None url = PROVIDER_URLS.get(slm.provider.lower()) if url is None: raise BadRequestException('Unknown provider') url = url.format(urlencode({'access_token': slm.access_token})) result = urlfetch.fetch(url) if result.status_code == 200: body = json.loads(result.content) social_id = body.get('id') # Determine if email provided, if any, is verified if slm.provider.lower() == 'facebook': # Can assume Facebook emails are verified: # http://stackoverflow.com/questions/14280535 # /is-it-possible-to-check-if-an-email-is-confirmed-on-facebook verified = True elif slm.provider.lower() == 'google': verified = body.get('verified_email') else: logging.error('Unexpected provider: ' + slm.provider) raise BadRequestException('Unknown provider') # Grab the social email and create a username based on the email # with most non-alphanumeric characters removed social_email = body.get('email') username = None if social_email: username = social_email.split('@')[0] username = re.sub('[^a-zA-Z0-9_-]+', '', username) if len(username) > 17: username = username[:17] if not username: username = '******' if not verified: # Don't actually use the social email if it is not verified social_email = None if social_id: # Need to fetch the user id associated with this social id # + email, or create a new user if one does not yet exist # Check if a user with this social id already exists auth_id = '{0}:{1}'.format(slm.provider.lower(), social_id) slm.user = User.get_by_auth_id(auth_id) if slm.user is None: # Social id not in use. Try getting user by verified email # to see if we can add social login with an existing user if social_email is not None: slm.user = User.get_by_email_verified(social_email) if slm.user is None: # Email not in use either. Create a new user. # Try creating a new user by varying the username for num in range(1000): suffix = '' if num > 0: suffix = str(num) this_username = username + suffix unique_properties = ['username'] if social_email is not None: unique_properties.append('email_verified') ok, info = User.create_user( auth_id, unique_properties=unique_properties, email_verified=social_email, email_pending=social_email, username=this_username, is_mod=False) if ok: slm.user = info break elif ('email' in info and social_email is not None): # Looks like the social email is in use after all. # This could happen, for instance, if a user tried # to double register at the same time. raise ConflictException( 'Email [' + social_email + '] for this account' + ' is already in use. ' + 'Did you accidentally try to login twice, ' + 'or have you not verified your email address' + ' yet?') else: # Failed to create an account after 1000 tries raise ConflictException( 'Encountered conflict when creating new account.') else: # Email is in use, but social_id is not. # If the User has a password, we require it before # adding the social auth id to the User if slm.user.has_password: if not slm.password: # Need a password, but none provided slm.password_required = True return slm if not slm.user.check_password(slm.password): # Need a password, but provided password invalid raise UnauthorizedException('Invalid credentials') # Now add the social auth id ok, info = slm.user.add_auth_id(auth_id) if ok: slm.user = info else: raise ConflictException( 'Encountered conflict when adding auth id to ' + 'existing account, conflicting properties: ' + str(info)) if (social_email and slm.user.email_pending_lower == social_email.lower() and slm.user.email_verified_lower != social_email.lower()): # Email is now verified by social login slm.user = self.validate_email_internal(slm.user) # Create auth token slm.user_id_auth_token = self.create_user_id_auth_token( slm.user.get_id()) if slm.user_id_auth_token is None: raise ConflictException( 'Encountered conflict when creating auth token') else: raise BadRequestException('Access token did not provide valid id') return slm
def Register(self, rm): # Validate request params invalid_params = dict() ok, msg = self.valid_username(rm.username) if not ok: invalid_params['username'] = msg ok, msg = self.valid_password(rm.password) if not ok: invalid_params['password'] = msg ok, msg = self.valid_email(rm.email) if not ok: invalid_params['email'] = msg if (config.USE_ACCESS_TOKENS and not User.validate_access_token(rm.access_token)): invalid_params['access_token'] = 'Invalid access token' if not rm.verification_url: invalid_params['verification_url'] = ( 'A verification url is required') if len(invalid_params.keys()) > 0: # Some params were invalid. Return 400 response. raise BadRequestException('|'.join([ key + ':' + invalid_params[key] for key in invalid_params.keys() ])) # Create the user with username auth id auth_id = self.get_local_auth_id(rm.username) ok, info = User.create_user(auth_id, password_raw=rm.password, unique_properties=[ 'email_pending', 'username', ], email_pending=rm.email, username=rm.username, is_mod=False) if ok: # Success. Add email auth id so that user can sign in # by email too user = info auth_id = self.get_local_auth_id(user.email_pending) ok, info = user.add_auth_id(auth_id) if not ok: logging.error('Failed to add email auth id [' + auth_id + ' ] to username [' + user.username + ']') # Send email verification user.send_email_verification(rm.verification_url) if config.USE_ACCESS_TOKENS: User.delete_access_token(rm.access_token) # Do any extra stuff needed upon user creation try: custom.user_created(user, rm.data) except: logging.error('Exception hit performing custom user created ' + 'work. Stack trace:\n{s}'.format( s=traceback.format_exc())) else: # Failed to create new user. Respond with conflicting properties # separated by a colon, converting auth_id to username info_set = set() for prop in info: if prop == 'auth_id': info_set.add('username') else: info_set.add(prop) raise ConflictException(':'.join(info_set)) return rm