def get_or_create_user_from_authid( self, auth_id, email = None, allow_create = True ): user = None user_with_same_auth_id = enki.libuser.get_EnkiUser_by_auth_id( auth_id ) if user_with_same_auth_id: # if a user with the same auth id already exists but has a blank email: add the email to the account. # note: if the account has an email or they've removed their email, we don't overwrite it. if email and user_with_same_auth_id.email == None: user = self.set_email( email, user_with_same_auth_id.key.id()) else: user = user_with_same_auth_id if not user and allow_create: # create a new user user = EnkiModelUser( email = email, auth_ids_provider = [ auth_id ]) user.put() return user
def set_email( self, email, user_id = None ): # set or change a user's email address user_key = enki.libuser.get_key_EnkiUser( email ) if email and (not user_key or user_key.id() == user_id): # if the email doesn't exist in the db or already belongs to the user: if user_id == None: # create a new user user = EnkiModelUser( email = email ) else: # update existing user user = ndb.Key( EnkiModelUser, user_id ).get() user.email = email user.put() return user else: return None
def set_email(self, email, user_id=None): # set or change a user's email address user_key = enki.libuser.get_key_EnkiUser(email) if email and (not user_key or user_key.id() == user_id): # if the email doesn't exist in the db or already belongs to the user: if user_id == None: # create a new user user = EnkiModelUser(email=email) else: # update existing user user = ndb.Key(EnkiModelUser, user_id).get() user.email = email user.put() return user else: return None
def delete_account(self, delete_posts=False, token=''): token_to_save = 'accountdelete' if not token: # there is no token if the user has no email address: they are deleted immediately. They must be logged in. user_to_delete = self.enki_user else: # a user has followed a accountdelete token link. The user account associated with the token will be deleted tokenEntity = EnkiModelTokenVerify.get_by_token(token) user_to_delete = EnkiModelUser.get_by_id(tokenEntity.user_id) # delete all user related tokens except any verify token related to account deletion that's not yet been used if tokenEntity.type == token_to_save: token_to_save = 'accountandpostsdelete' verify_tokens_to_delete = EnkiModelTokenVerify.fetch_keys_by_user_id_except_type( user_to_delete.key.id(), token_to_save) if verify_tokens_to_delete: ndb.delete_multi(verify_tokens_to_delete) email_rollback_tokens_to_delete = EnkiModelTokenEmailRollback.fetch_keys_by_user_id( user_to_delete.key.id()) if email_rollback_tokens_to_delete: ndb.delete_multi(email_rollback_tokens_to_delete) # Delete the user account and log them out. if not HandlerBase.account_is_active(user_to_delete.key.id()): # delete user if the account is inactive display_names = EnkiModelDisplayName.fetch_keys_by_user_id( user_to_delete.key.id()) if display_names: ndb.delete_multi(display_names) user_to_delete.key.delete() else: # anonymise the user if user_to_delete.email: # delete email subscriptions EnkiModelEmailSubscriptions.remove_by_email( user_to_delete.email) user_to_delete.email = None if user_to_delete.password: user_to_delete.password = None if user_to_delete.auth_ids_provider: user_to_delete.auth_ids_provider = [] user_to_delete.put() # keep all historical display_names. Add a new current display_name '[deleted]' (unless it's already been deleted) display_name = EnkiModelDisplayName.get_by_user_id_current( user_to_delete.key.id()) if display_name: if display_name.prefix != EnkiModelDisplayName.DELETED_PREFIX or display_name.suffix != EnkiModelDisplayName.DELETED_SUFFIX: EnkiModelDisplayName.set_display_name( user_to_delete.key.id(), EnkiModelDisplayName.DELETED_PREFIX, EnkiModelDisplayName.DELETED_SUFFIX) # delete user's sent and received messages EnkiModelMessage.delete_user_messages(user_to_delete.key.id()) # delete user's posts if required if delete_posts: EnkiModelPost.delete_user_posts(user_to_delete.key.id()) # log the deleted user out if self.enki_user == user_to_delete.key.id(): self.log_out() EnkiModelTokenAuth.revoke_user_authentications(user_to_delete.key.id())
def get_or_create_user_from_authid(self, auth_id, email=None, allow_create=True): user = None user_with_same_auth_id = EnkiModelUser.get_by_auth_id(auth_id) if user_with_same_auth_id: # if a user with the same auth id already exists but has a blank email: add the email to the account. # note: if the account has an email or they've removed their email, we don't overwrite it. if email and user_with_same_auth_id.email == None: user = self.set_email(email, user_with_same_auth_id.key.id()) else: user = user_with_same_auth_id if not user and allow_create: # create a new user user = EnkiModelUser(email=email, auth_ids_provider=[auth_id]) user.put() return user
def log_in_with_email(self, email, password): # log the user in using their email if EnkiModelBackoffTimer.get(email, True) == 0: user = EnkiModelUser.get_by_email(email) if user and user.password: validPassword = enki.authcryptcontext.pwd_context.verify( password, user.password) if validPassword: self.log_in_session_token_create(user) EnkiModelBackoffTimer.remove(user.email) return True return False
def get_data( self, handler ): useridnumber = handler.request.route_kwargs.get( 'useridnumber' ) data = {} data[ 'posts' ] = '' data[ 'is_author' ] = False if handler.ensure_is_logged_in(): if useridnumber.isdigit() and EnkiModelUser.get_by_id( int( useridnumber ) ): posts = EnkiModelPost.get_author_posts( useridnumber ) if posts: data[ 'posts' ] = posts data[ 'is_author' ] = True if handler.user_id == posts.author_data.user_id else False return data
def reauthenticate(self, email, password): # reauthenticate the user if EnkiModelBackoffTimer.get(email, True) == 0: user = EnkiModelUser.get_by_email(email) if user and user.password: validPassword = enki.authcryptcontext.pwd_context.verify( password, user.password) if validPassword and self.is_logged_in( ) and self.user_id == user.key.id(): self.session['reauth_time'] = datetime.datetime.now() EnkiModelBackoffTimer.remove(user.email) return True return False
def set_auth_id(self, auth_id, user_id): # add a new auth Id to an existing account user_has_same_auth_id = EnkiModelUser.exist_by_auth_id(auth_id) if not user_has_same_auth_id: user = ndb.Key(EnkiModelUser, user_id).get() if user: # add the auth_id to the account user.auth_ids_provider.append(auth_id) user.put() return user else: return None else: return None
def get_or_create_user_from_authid( self, authId, email = None, allow_create = False ): user = None user_with_same_auth_id = EnkiModelUser.query( EnkiModelUser.auth_ids_provider == authId ).get() if user_with_same_auth_id: # if a user with the same auth id already exists but has a blank email: add the email to the account. # note: if the account has an email, we don't overwrite it. if email and user_with_same_auth_id.email == None: user = self.set_email( email, user_with_same_auth_id.key.id()) else: user = user_with_same_auth_id elif email: # no user with the same auth id, but there is a user with the same email: add the auth id to the account user_with_same_email = EnkiModelUser.query( EnkiModelUser.email == email ).get() if user_with_same_email: colon = authId.find( ':' ) provider_name = str( authId[ :colon ]) provider_uid = str( authId[ colon+1: ]) self.send_email( email, MSG.SEND_EMAIL_AUTH_NEW_SUBJECT(), MSG.SEND_EMAIL_AUTH_NEW_BODY( enki.libutil.get_local_url( 'profile' ), provider_name, provider_uid ) ) user = self.set_authid( authId, user_with_same_email.key.id()) if not user and allow_create: # create a new user user = EnkiModelUser( email = email, auth_ids_provider = [ authId ]) user.put() return user
def delete_account( self, delete_posts = False, token = '' ): token_to_save = 'accountdelete' if not token: # there is no token if the user has no email address: they are deleted immediately. They must be logged in. user_to_delete = self.enki_user else: # a user has followed a accountdelete token link. The user account associated with the token will be deleted tokenEntity = EnkiModelTokenVerify.get_by_token( token ) user_to_delete = EnkiModelUser.get_by_id( tokenEntity.user_id ) # delete all user related tokens except any verify token related to account deletion that's not yet been used if tokenEntity.type == token_to_save: token_to_save = 'accountandpostsdelete' verify_tokens_to_delete = EnkiModelTokenVerify.fetch_keys_by_user_id_except_type( user_to_delete.key.id(), token_to_save ) if verify_tokens_to_delete: ndb.delete_multi( verify_tokens_to_delete ) email_rollback_tokens_to_delete = enki.libuser.fetch_keys_RollbackToken( user_to_delete.key.id()) if email_rollback_tokens_to_delete: ndb.delete_multi( email_rollback_tokens_to_delete ) # Delete the user account and log them out. if not HandlerBase.account_is_active( user_to_delete.key.id()): # delete user if the account is inactive display_names = enki.libdisplayname.fetch_EnkiUserDisplayName_by_user_id( user_to_delete.key.id()) if display_names: ndb.delete_multi( display_names ) user_to_delete.key.delete() else: # anonymise the user if user_to_delete.email: user_to_delete.email = None if user_to_delete.password: user_to_delete.password = None if user_to_delete.auth_ids_provider: user_to_delete.auth_ids_provider = [] user_to_delete.put() # keep all historical display_names. Add a new current display_name '[deleted]' (unless it's already been deleted) display_name = enki.libdisplayname.get_EnkiUserDisplayName_by_user_id_current( user_to_delete.key.id()) if display_name: if display_name.prefix != enki.libdisplayname.DELETED_PREFIX or display_name.suffix != enki.libdisplayname.DELETED_SUFFIX: enki.libdisplayname.set_display_name( user_to_delete.key.id(), enki.libdisplayname.DELETED_PREFIX, enki.libdisplayname.DELETED_SUFFIX ) # delete user's sent and received messages ndb.delete_multi( enki.libmessage.fetch_keys_sent_or_received_message( user_to_delete.key.id())) # delete user's posts if required if delete_posts: enki.libforum.delete_user_posts( user_to_delete.key.id()) # log the deleted user out if self.enki_user == user_to_delete.key.id(): self.log_out() enki.libuser.revoke_user_authentications( user_to_delete.key.id())
def password_change_request(self, email): if EnkiModelUser.exist_by_email(email): # create an email verify token, send it to the email address token = security.generate_random_string(entropy=256) emailToken = EnkiModelTokenVerify(token=token, email=email, type='passwordchange') emailToken.put() link = enki.libutil.get_local_url( 'passwordrecoverconfirm', {'verifytoken': emailToken.token}) self.send_email(email, MSG.SEND_EMAIL_PASSWORD_RESET_SUBJECT(), MSG.SEND_EMAIL_PASSWORD_RESET_BODY(link)) result = enki.libutil.ENKILIB_OK else: result = self.ERROR_EMAIL_NOT_EXIST return result
def email_set_request(self, email): # request the creation of a new account based on an email address result = enki.libutil.ENKILIB_OK if EnkiModelUser.exist_by_email(email): result = self.ERROR_EMAIL_IN_USE else: # create an email verify token, send it to the email address token = security.generate_random_string(entropy=256) emailToken = EnkiModelTokenVerify(token=token, email=email, type='register') emailToken.put() link = enki.libutil.get_local_url( 'registerconfirm', {'verifytoken': emailToken.token}) self.send_email(email, MSG.SEND_EMAIL_REGISTER_CONFIRM_SUBJECT(), MSG.SEND_EMAIL_REGISTER_CONFIRM_BODY(link)) return result
def get_EnkiUser(email): entity = EnkiModelUser.query(EnkiModelUser.email == email).get() if entity: return entity else: return None
def provider_authenticated_callback(self, loginInfo): # We expect the fields of the dictionary to be: # - 'provider_name' unique 'pretty' provider name (e.g. google, facebook,...) # - 'provider_uid' provider specific (a.k.a "locally unique") user Id, i.e unique to the provider (e.g. the google user id number) # - 'email' # - 'email_verified' # We IGNORE: username, gender (facebook), avatar link, etc. # get the verified email from the auth provider email = None if loginInfo['email'] and loginInfo['email_verified'] == True: email = loginInfo['email'] # get the authId from the auth provider auth_id = loginInfo['provider_name'] + ':' + loginInfo['provider_uid'] if auth_id: # Modify existing or create user # check if it's an add login method request LoginAddToken = EnkiModelTokenVerify.get_by_user_id_state_type( self.user_id, loginInfo['provider_name'], 'loginaddconfirm_1') if LoginAddToken: # Add a login method if not EnkiModelUser.exist_by_auth_id(auth_id): # store the new auth prov + id in the session LoginAddToken.state = auth_id LoginAddToken.type = 'loginaddconfirm_2' LoginAddToken.put() self.redirect( enki.libutil.get_local_url('loginaddconfirm')) else: self.add_infomessage( MSG.INFORMATION(), MSG.AUTH_PROVIDER_CANNOT_BE_ADDED(str(auth_id))) self.redirect(enki.libutil.get_local_url('accountconnect')) return else: user = self.get_user_from_authid(auth_id, email) if user: # Existing authentication method / user if self.is_logged_in() and self.user_id == user.key.id(): # Refresh the reauthenticated status self.session['reauth_time'] = datetime.datetime.now() self.add_infomessage(MSG.SUCCESS(), MSG.REAUTHENTICATED()) self.redirect_to_relevant_page() return # Login self.log_in_session_token_create(user) self.add_infomessage(MSG.SUCCESS(), MSG.LOGGED_IN()) self.redirect_to_relevant_page() else: # New authentication method register_token = EnkiModelTokenVerify.get_by_state_type( auth_id, 'register') if register_token: # If a token already exists, get the token value and update the email token = register_token.token register_token.email = email # update in case the user changed their email or modified their email access permission else: # Create a new token token = security.generate_random_string(entropy=256) register_token = EnkiModelTokenVerify(token=token, email=email, state=auth_id, type='register') register_token.put() self.session['tokenregisterauth'] = token if EnkiModelUser.exist_by_email(email): self.redirect( enki.libutil.get_local_url( 'registeroauthwithexistingemail')) else: self.redirect( enki.libutil.get_local_url('registeroauthconfirm')) else: self.redirect_to_relevant_page()
def get_EnkiUser( email ): entity = EnkiModelUser.query( EnkiModelUser.email == email ).get() return entity
def email_change_request(self, email): # request an email address to be modified. Create a rollback option. result = 'cannot_remove' emailCurrent = self.enki_user.email userId = self.enki_user.key.id() if email != '' and EnkiModelUser.exist_by_email(email): # if the new email matches an existing verified user email, reject it if emailCurrent == email: result = 'same' else: result = self.ERROR_EMAIL_IN_USE # Note: send an email to emailcurrent regardless to prevent email checking (see below) else: if email == '': # if the user erased the email, and they can log in through auth, store "removed" in the email field, so it isn't overwritten by an auth login with a verified email if self.enki_user.auth_ids_provider: self.enki_user.email = 'removed' self.enki_user.put() result = 'removed' else: return result else: # email the new, unverified address with a link to allow the user to verify the email tokenEntity = EnkiModelTokenVerify.get_by_user_id_email_type( userId, email, 'emailchange') if tokenEntity: # if a verify token for the same new email address and user already exists, use its token token = tokenEntity.token else: # otherwise create a new token token = security.generate_random_string(entropy=256) emailToken = EnkiModelTokenVerify(token=token, email=email, user_id=userId, type='emailchange') emailToken.put() link = enki.libutil.get_local_url('emailchangeconfirm', {'verifytoken': token}) self.send_email( email, MSG.SEND_EMAIL_EMAIL_CHANGE_CONFIRM_SUBJECT(), MSG.SEND_EMAIL_EMAIL_CHANGE_CONFIRM_BODY(link, email)) result = 'change' if emailCurrent and emailCurrent != 'removed' and result != 'same': # email the current, verified address in case they want to undo the change (useful if account has been hacked) # skip this step if the current email is empty (case if user logged in with auth id without email with e.g. Steam) or "removed". # If the email is already in use, mask the fact to prevent email checking. tokenEntity = EnkiModelTokenEmailRollback.get_by_user_id_email( userId, emailCurrent) if tokenEntity: # if the old email is already in the archive, use its token token = tokenEntity.token else: # otherwise create a new token token = security.generate_random_string(entropy=256) emailOldToken = EnkiModelTokenEmailRollback(token=token, email=emailCurrent, user_id=userId) emailOldToken.put() if result == self.ERROR_EMAIL_IN_USE: self.add_debugmessage( '''Comment - whether the email is available or not, the feedback through both the UI AND EMAIL is identical to prevent email checking.''' ) link = enki.libutil.get_local_url('emailrollback', {'rollbacktoken': token}) self.send_email( emailCurrent, MSG.SEND_EMAIL_EMAIL_CHANGE_UNDO_SUBJECT(), MSG.SEND_EMAIL_EMAIL_CHANGE_UNDO_BODY(link, emailCurrent)) return result
def get_key_EnkiUser( email ): key = EnkiModelUser.query( EnkiModelUser.email == email ).get( keys_only = True ) return key
def exist_EnkiUser( email ): count = EnkiModelUser.query( EnkiModelUser.email == email ).count( 1 ) return count > 0
def exist_EnkiUser(email): count = EnkiModelUser.query(EnkiModelUser.email == email).count(1) if count: return True else: return False
def get_key_EnkiUser(email): key = EnkiModelUser.query(EnkiModelUser.email == email).get(keys_only=True) if key: return key else: return None
def get_EnkiUser_by_auth_id( auth_id ): entity = EnkiModelUser.query( EnkiModelUser.auth_ids_provider == auth_id ).get() return entity
def exist_Auth_Id( auth_id ): count = EnkiModelUser.query( EnkiModelUser.auth_ids_provider == auth_id ).count( 1 ) return count > 0