Example #1
0
	def verifyEmailToken(self, request):
		""" Verify email token, to verify email address. Return email address string or 'error' """
		status = StringMsg()  # return status
		status.data = 'error'  # default to error

		# Decode the JWT token
		try:
			payload = jwt.decode(request.accessToken, EMAIL_VERIF_SECRET, algorithm='HS256')
		except:
			return status

		# If valid JWT token, extract the info and update DB if applicable
		user_id = payload['userId']
		email = payload['contactEmail']

		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# If user changed email and clicked on old email verif link, this request is invalid
		if profile.contactEmail != email:
			return status

		# If we get here then email is verified. Update DB and return successful status
		profile.emailVerified = True
		profile.put()

		status.data = email
		return status
Example #2
0
	def changePassword(self, request):
		""" Change password """
		status = StringMsg()
		status.data = 'error'

		# Get user profile
		user_id = self._getUserId(request.accessToken)
		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# Not sure how this would happen, but it would be an error
		if not profile:
			return status

		# Check if provided old password matches user's current password
		db_salt, db_passkey = profile.salt_passkey.split('|')
		passkey = KDF.PBKDF2(request.oldPw, db_salt.decode('hex')).encode('hex')

		# Passwords don't match, return
		if passkey != db_passkey:
			status.data = 'old_pw_wrong'
			return status

		# If passwords match, salt & hash new password
		new_salt = Crypto.Random.new().read(16)
		new_passkey = KDF.PBKDF2(request.newPw, new_salt).encode('hex')
		new_salt_passkey = new_salt.encode('hex') + '|' + new_passkey
		profile.salt_passkey = new_salt_passkey

		# Also generate new session ID
		session_id = Crypto.Random.new().read(16).encode('hex')
		profile.session_id = session_id

		# Update DB
		profile.put()

		# Send user an email to notify password change
		self._emailPwChange(profile)

		# Return success status
		status.data = 'success'
		return status
Example #3
0
	def createAccount(self, request):
		""" Create new custom account """
		status = StringMsg()  # return status
		status.data = 'error'  # default to error

		# Verify if user passed reCAPTCHA
		# POST request to Google reCAPTCHA API
		url = 'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s' % (GRECAPTCHA_SECRET, request.recaptcha)
		try:
			result = urlfetch.Fetch(url, method=2)
		except:
			raise endpoints.BadRequestException('urlfetch error: Unable to POST to Google reCAPTCHA')
			return status
		data = json.loads(result.content)
		if not data['success']:
			status.data = 'recaptcha_fail'
			return status

		user_id = 'ca_' + request.email

		# Get profile from datastore -- if profile not found, then profile=None
		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# If profile exists, return status
		if profile:
			status.data = 'user_exists'
			return status

		# Salt and hash the password
		salt = Crypto.Random.new().read(16)
		passkey = KDF.PBKDF2(request.password, salt).encode('hex')

		salt_passkey = salt.encode('hex') + '|' + passkey

		# Generate new session ID
		session_id = Crypto.Random.new().read(16).encode('hex')

		# Create new profile for user
		Profile(
			key = profile_key,
			userId = user_id,
			contactEmail = request.email,
			salt_passkey = salt_passkey,
			session_id = session_id,
			loggedIn = True,
			emailVerified = False,
			notifications = [False, True]
		).put()

		# Generate user access token
		token = self._genToken({'userId': user_id, 'session_id': session_id})

		# If we get here, means we suceeded
		status.data = 'success'
		status.accessToken = token
		return status
Example #4
0
	def resetPassword(self, request):
		""" Reset password, verify token. Return status. """
		status = StringMsg()
		status.data = 'error'

		# Validate and decode token
		try:
			payload = jwt.decode(request.accessToken, CA_SECRET, algorithm='HS256')
		except:
			status.data = 'invalid_token'
			return status

		# Get user profile
		user_id = payload['userId']
		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# Not sure how this would happen, but it would be an error
		if not profile:
			return status

		# Salt & hash new password
		new_salt = Crypto.Random.new().read(16)
		new_passkey = KDF.PBKDF2(request.data, new_salt).encode('hex')
		new_salt_passkey = new_salt.encode('hex') + '|' + new_passkey
		profile.salt_passkey = new_salt_passkey

		# Also generate new session ID
		session_id = Crypto.Random.new().read(16).encode('hex')
		profile.session_id = session_id

		# Update DB
		profile.put()

		# Send user an email to notify password change
		self._emailPwChange(profile)

		# Return success status
		status.data = 'success'
		return status
Example #5
0
	def _updateProfile(self, request):
		"""Update user profile."""
		status = StringMsg()
		status.data = 'normal'

		token = request.accessToken
		user_id = self._getUserId(token)

		# Make sure the incoming message is initialized, raise exception if not
		request.check_initialized()

		# Get existing profile from datastore
		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# Check if email changed. Note custom account users cannot change email.
		email_change = (profile.contactEmail != request.contactEmail) and (user_id[:3] != 'ca_')

		# Update profile object from the user's form
		for field in request.all_fields():
			if field.name == 'userId':
				continue  # userId is fixed
			elif user_id[:3] == 'ca_' and field.name == 'contactEmail':
				continue  # custom account users cannot change email address
			elif field.name != 'accessToken':
				setattr(profile, field.name, getattr(request, field.name))

		# If this is user's first time updating profile, or changing email address
		# then send email verification
		if profile.pristine or email_change:
			profile.pristine = False
			self._emailVerif(profile)

			status.data = 'email_verif'

		# Save updated profile to datastore
		profile.put()

		return status
Example #6
0
	def login(self, request):
		""" Check username/password to login """
		status = StringMsg()  # return status
		status.data = 'error'  # default to error

		# Verify if user passed reCAPTCHA
		# POST request to Google reCAPTCHA API
		url = 'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s' % (GRECAPTCHA_SECRET, request.recaptcha)
		try:
			result = urlfetch.Fetch(url, method=2)
		except:
			raise endpoints.BadRequestException('urlfetch error: Unable to POST to Google reCAPTCHA')
			return status
		data = json.loads(result.content)
		if not data['success']:
			status.data = 'recaptcha_fail'
			return status

		user_id = 'ca_' + request.email

		# Get profile from datastore -- if profile not found, then profile=None
		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# If profile does not exist, return False
		if not profile:
			return status

		# Parse salt and passkey from DB, compare it to provided version
		db_salt, db_passkey = profile.salt_passkey.split('|')
		passkey = KDF.PBKDF2(request.password, db_salt.decode('hex')).encode('hex')

		# Passwords don't match, return False
		if passkey != db_passkey:
			return status

		# Generate new session ID
		session_id = Crypto.Random.new().read(16).encode('hex')
		profile.session_id = session_id

		# Update user's status to logged-in
		profile.loggedIn = True
		profile.put()

		# Generate user access token
		token = self._genToken({'userId': user_id, 'session_id': session_id})

		# If we get here, means we suceeded
		status.data = 'success'
		status.accessToken = token
		return status
Example #7
0
	def forgotPassword(self, request):
		""" Forgot password, send user password reset link via email """
		status = StringMsg()
		status.data = 'error'

		# Verify if user passed reCAPTCHA
		# POST request to Google reCAPTCHA API
		url = 'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s' % (GRECAPTCHA_SECRET, request.recaptcha)
		try:
			result = urlfetch.Fetch(url, method=2)
		except:
			raise endpoints.BadRequestException('urlfetch error: Unable to POST to Google reCAPTCHA')
			return status
		data = json.loads(result.content)
		if not data['success']:
			status.data = 'recaptcha_fail'
			return status

		user_id = 'ca_' + request.email

		# Get profile from datastore -- if profile not found, then profile=None
		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# Check if profile exists. If not, return status
		if not profile:
			status.data = 'invalid_email'
			return status

		# If email unverified, return status
		if not profile.emailVerified:
			status.data = 'unverified_email'
			return status

		# Send password reset link to user's email
		if self._emailPwReset(profile):
			status.data = 'success'

		return status
Example #8
0
	def fbLogin(self, request):
		""" Handle Facebook login """
		status = StringMsg()  # return status message
		status.data = 'error'  # default to error message, unless specified otherwise
		'''
		# Swap short-lived token for long-lived token
		short_token = request.data

		url = 'https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id=%s&client_secret=%s&fb_exchange_token=%s' % (
			FB_APP_ID, FB_APP_SECRET, short_token)
		try:
			result = urlfetch.Fetch(url, method=1)
		except:
			print('urlfetch error1')
			return status

		token = result.content.split('&')[0]  # 'access_token=blahblahblah'
		'''
		token = request.accessToken

		# Use token to get user info from API
		url = 'https://graph.facebook.com/v%s/me?access_token=%s&fields=name,id,email' % (FB_API_VERSION, token)
		try:
			result = urlfetch.Fetch(url, method=1)
		except:
			raise endpoints.BadRequestException('urlfetch error')
			return status

		data = json.loads(result.content)

		if 'error' in data:
			raise endpoints.BadRequestException('FB OAuth token error')
			return status

		user_id = 'fb_' + data['id']
		first_name = data['name'].split()[0]
		last_name = data['name'].split()[-1]
		email = data['email']

		# Get existing profile from datastore, or create new one
		profile_key = ndb.Key(Profile, user_id)
		profile = profile_key.get()

		# If profile already exists, return 'existing_user'
		# Unless, they have empty firstName (maybe they got d/c'ed on profile page)
		if profile:
			# If empty first name, return new_user
			if profile.firstName == '':
				status.data = 'new_user'
				return status

			# If user previously logged-out, update login status in NDB
			if not profile.loggedIn:
				profile.loggedIn = True
				profile.put()

			status.data = 'existing_user'
			return status

		# Else, create new profile and return 'new_user'
		profile = Profile(
			key = profile_key,
			userId = user_id,
			contactEmail = email,
			firstName = first_name,
			lastName = last_name,
			loggedIn = True,
			emailVerified = False,
			notifications = [True, False]
		).put()

		status.data = 'new_user'
		return status