def password_recovery_request(): """Start password recovery process. Shows a form to user and allows to request password dropping via email Recovery tokens are stored in local MySQL db 'invitations' """ form = forms.PasswordRecoveryRequest() if form.validate_on_submit(): # check if user exasts in database try: utils.neo4j_api_call( '/users', {"email": form.email.data}, 'GET')[0] except (KeyError, NotFound): flask.flash( 'User with that email "%s" is not registered.' % form.email.data, 'error') exc_type, exc_value, traceback = sys.exc_info() flask.current_app.log_exception((exc_type, exc_value, traceback)) else: hash_code = str(uuid.uuid4()) recovery_link = flask.url_for('password_recovery_finish', recovery_hash=hash_code) row_mysql_queries.save_recovery(form.email.data, hash_code, 0) msg = mail.Message( 'Password recovery', recipients=[form.email.data]) msg.body = flask.render_template('RecoveryPasswordEmail/body.txt', recovery_link=recovery_link) utils.send_msg(msg) flask.flash('Recovery request was sent successfully', 'info') if 'wrong_email' in flask.session: form.email.data = flask.session['wrong_email'] return {'form': form}
def delete(): """Delete user. Removes user from Keystone database. TODO(apugachev): pass user_id in path, make form empty just for CSRF sake. This provides better tracking via HTTPD logs. """ form = forms.DeleteUserForm() if form.validate_on_submit(): keystone_user = clients.admin_clients().keystone.users.get( form.user_id.data) if keystone_user.email: odb_user = utils.neo4j_api_call('/users', {"email": keystone_user.email}, 'GET')[0] else: odb_user_list = utils.neo4j_api_call('/users', method='GET') odb_user = filter(lambda x: x.username == keystone_user.name, odb_user_list) utils.neo4j_api_call('/users/%s' % odb_user['id'], method='DELETE') roles = keystone_user.list_roles() for role in roles: clients.admin_clients().keystone.tenants.remove_user( role.tenant['id'], keystone_user, role.role['id']) keystone_user.delete() flask.flash('User was deleted.', 'success') else: flask.flash('User was not deleted', 'error') return flask.redirect(flask.url_for('.index'))
def password_recovery_finish(recovery_hash): """ This will be called after user clicked link in email. """ try: id, email, hash_code, complete = \ row_mysql_queries.get_recovery_request_by_hash(recovery_hash) except TypeError: # db returns None flask.abort(404) if complete == 1: flask.flash('Password recovery token is expired', 'error') return flask.redirect(flask.url_for('dashboard')) odb_user = utils.neo4j_api_call('/users', {"email": email}, 'GET')[0] new_hash = str(uuid.uuid4()) # set trash password in keystone keystone_user = utils.get_keystone_user_by_username(odb_user['username']) clients.admin_clients().keystone.users.update_password(keystone_user, new_hash) # set trash password in odb utils.neo4j_api_call('/users', { 'id': odb_user['id'], 'login': odb_user['login'], 'username': odb_user['username'], 'email': odb_user['email'], 'passwordHash': utils.create_hashed_password(new_hash)}, 'PUT') # send trash password back to user msg = mail.Message('Password recovery', recipients=[odb_user['email']]) msg.body = flask.render_template('RecoveryPasswordFinishEmail/body.txt', new_pass=new_hash) utils.send_msg(msg) flask.flash('New password was sent to you', 'success') return flask.redirect(flask.url_for('dashboard'))
def delete(): """Delete user. Removes user from Keystone database. TODO(apugachev): pass user_id in path, make form empty just for CSRF sake. This provides better tracking via HTTPD logs. """ form = forms.DeleteUserForm() if form.validate_on_submit(): keystone_user = clients.admin_clients().keystone.users.get( form.user_id.data) if keystone_user.email: odb_user = utils.neo4j_api_call('/users', { "email": keystone_user.email }, 'GET')[0] else: odb_user_list = utils.neo4j_api_call('/users', method='GET') odb_user = filter( lambda x: x.username == keystone_user.name, odb_user_list) utils.neo4j_api_call('/users/%s' % odb_user['id'], method='DELETE') roles = keystone_user.list_roles() for role in roles: clients.admin_clients().keystone.tenants.remove_user( role.tenant['id'], keystone_user, role.role['id']) keystone_user.delete() flask.flash('User was deleted.', 'success') else: flask.flash('User was not deleted', 'error') return flask.redirect(flask.url_for('.index'))
def password_recovery_request(): """Start password recovery process. Shows a form to user and allows to request password dropping via email Recovery tokens are stored in local MySQL db 'invitations' """ form = forms.PasswordRecoveryRequest() if form.validate_on_submit(): # check if user exasts in database try: utils.neo4j_api_call('/users', {"email": form.email.data}, 'GET')[0] except (KeyError, NotFound): flask.flash( 'User with that email "%s" is not registered.' % form.email.data, 'error') exc_type, exc_value, traceback = sys.exc_info() flask.current_app.log_exception((exc_type, exc_value, traceback)) else: hash_code = str(uuid.uuid4()) recovery_link = flask.url_for('password_recovery_finish', recovery_hash=hash_code) row_mysql_queries.save_recovery(form.email.data, hash_code, 0) msg = mail.Message('Password recovery', recipients=[form.email.data]) msg.body = flask.render_template('RecoveryPasswordEmail/body.txt', recovery_link=recovery_link) utils.send_msg(msg) flask.flash('Recovery request was sent successfully', 'info') if 'wrong_email' in flask.session: form.email.data = flask.session['wrong_email'] return {'form': form}
def finish(invitation_hash): """Finish invitation process. Check everything, create user, redirect to logout (it wipes out session and redirects to login). """ try: invitation_id, email, hash_code, complete, role = \ row_mysql_queries.get_invitation_by_hash(invitation_hash) except TypeError: # hash code not found, None returned # TODO(apugachev) show form to typ e invitation token flask.flash('Invitation token not found.', 'error') return flask.redirect(flask.url_for('dashboard')) if complete == 1: flask.flash('Invitation token is already used.', 'error') return flask.redirect(flask.url_for('dashboard')) form = forms.InviteRegister() username = email.split("@")[0] form.email.data = email username_is_taken = False try: utils.neo4j_api_call('/users', { "email": email }, 'GET')[0] flask.flash( 'This username is already taken. Please, choose another one.', 'warning') # NOTE(apugachev) at the beginning 'username' is HiddenField # now we want to allow user to edit username # that's why bind() username_field = wtf.TextField( 'Username', [wtf.Required()]).bind(form, 'username') form.username = username_field form._fields['username'] = username_field username_is_taken = True except Exception: # NOTE(apugachev) user not found, success; # TODO(apugachev) find out what exceptions can occur and what to do if form.validate_on_submit(): new_odb_user = register_user( form.username.data, form.email.data, form.password.data, role) if new_odb_user is not None: row_mysql_queries.update_invitation( invitation_id, email, hash_code) # NOTE(apugachev)no flash, it gets deleted on logout return flask.redirect(flask.url_for('logout')) form.username.data = username return { 'form': form, 'email': email, 'username': username, 'username_is_taken': username_is_taken }
def finish(invitation_hash): """Finish invitation process. Check everything, create user, redirect to logout (it wipes out session and redirects to login). """ try: invitation_id, email, hash_code, complete, role = \ row_mysql_queries.get_invitation_by_hash(invitation_hash) except TypeError: # hash code not found, None returned # TODO(apugachev) show form to typ e invitation token flask.flash('Invitation token not found.', 'error') return flask.redirect(flask.url_for('dashboard')) if complete == 1: flask.flash('Invitation token is already used.', 'error') return flask.redirect(flask.url_for('dashboard')) form = forms.InviteRegister() username = email.split("@")[0] form.email.data = email username_is_taken = False try: utils.neo4j_api_call('/users', {"email": email}, 'GET')[0] flask.flash( 'This username is already taken. Please, choose another one.', 'warning') # NOTE(apugachev) at the beginning 'username' is HiddenField # now we want to allow user to edit username # that's why bind() username_field = wtf.TextField('Username', [wtf.Required()]).bind( form, 'username') form.username = username_field form._fields['username'] = username_field username_is_taken = True except Exception: # NOTE(apugachev) user not found, success; # TODO(apugachev) find out what exceptions can occur and what to do if form.validate_on_submit(): new_odb_user = register_user(form.username.data, form.email.data, form.password.data, role) if new_odb_user is not None: row_mysql_queries.update_invitation(invitation_id, email, hash_code) # NOTE(apugachev)no flash, it gets deleted on logout return flask.redirect(flask.url_for('logout')) form.username.data = username return { 'form': form, 'email': email, 'username': username, 'username_is_taken': username_is_taken }
def _login(username, password): try: odb_user = utils.neo4j_api_call('/users', { "email": username, }, 'GET')[0] except NotFound: # NOTE(apugachev) ODB returns 404 for non-existing email, lol. return False if not (odb_user['passwordHash'] == utils.create_hashed_password(password)): return False username = odb_user['username'] # NOTE(apugachev) # odb username is Keystone user name # password is the same as Keystone password try: clients.create_unscoped(username, password) except Unauthorized: return False flask.session['user'] = ( flask.session['keystone_unscoped']['access']['user']) flask.g.is_authenticated = True flask.flash('You were logged in successfully.', 'success') user_tenants = utils.user_tenants_list( utils.get_keystone_user_by_username(username)) flask.session['tenants'] = user_tenants # NOTE(apugachev) # Principal identity name is Keystone user id principal.identity_changed.send( focus.app, identity=principal.Identity( flask.session['keystone_unscoped'][ 'access']['user']['id'])) return True
def configured_hostname(): ''' Set hostname to use in all links set via email. ''' form = forms.ConfigureHostnameForm() if form.validate_on_submit(): # save row_mysql_queries.set_configured_hostname(form.hostname.data) # send username = flask.session['user']['username'] email = utils.neo4j_api_call( '/users/?username=%s' % username)[0]['email'] msg = mail.Message( 'New Hostname Confiured', recipients=[email]) msg.body = flask.render_template( 'ConfigureHostnameEmail/body.txt') utils.send_msg(msg) # notify flask.flash('Hostname %s configured' % form.hostname.data, 'success') # redirect return flask.redirect(flask.url_for('.configured_hostname')) return { 'form': form, 'title': 'Focus URL', 'subtitle': 'Network address of Altai UI', }
def _login(username, password): try: odb_user = utils.neo4j_api_call('/users', { "email": username, }, 'GET')[0] except NotFound: # NOTE(apugachev) ODB returns 404 for non-existing email, lol. return False if not (odb_user['passwordHash'] == utils.create_hashed_password(password)): return False username = odb_user['username'] # NOTE(apugachev) # odb username is Keystone user name # password is the same as Keystone password try: clients.create_unscoped(username, password) except Unauthorized: return False flask.session['user'] = ( flask.session['keystone_unscoped']['access']['user']) flask.g.is_authenticated = True flask.flash('You were logged in successfully.', 'success') user_tenants = utils.user_tenants_list( utils.get_keystone_user_by_username(username)) flask.session['tenants'] = user_tenants # NOTE(apugachev) # Principal identity name is Keystone user id principal.identity_changed.send( focus.app, identity=principal.Identity( flask.session['keystone_unscoped']['access']['user']['id'])) return True
def _register_in_ODB(username, email, password): """Register user in ODB. API 'create_user' call to ODB, then read new user from ODB and \ returns it. """ # new user utils.neo4j_api_call( '/users', { "login": "", "username": username, "email": email, "passwordHash": utils.create_hashed_password(password), }, 'POST') # return fresh user user = utils.neo4j_api_call('/users', {"email": email}, 'GET')[0] return user
def _register_in_ODB(username, email, password): """Register user in ODB. API 'create_user' call to ODB, then read new user from ODB and \ returns it. """ # new user utils.neo4j_api_call('/users', { "login": "", "username": username, "email": email, "passwordHash": utils.create_hashed_password(password), }, 'POST') # return fresh user user = utils.neo4j_api_call('/users', { "email": email }, 'GET')[0] return user
def invite(): """Send invitation. Admins are allowed to invite admins and members, members - only members. """ masks = row_mysql_queries.get_masks() form = forms.Invite() if form.validate_on_submit(): # Check if required conf setting exists _conf = flask.current_app.config if not 'KEYSTONE_CONF' in _conf or not 'NEO4J_API_URL' in _conf: raise Exception("""No required settings: KEYSTONE_CONF or NEO4J_API_URL, invitations wasn't sent""", "") user_email = form.email.data if not utils.username_is_taken(user_email): try: utils.neo4j_api_call('/users', { "email": user_email }, 'GET')[0] flask.flash( 'User with email "%s" is already registered' % user_email, 'error') except (KeyError, NotFound): # NOTE(apugachev) success, user does not exist hash_code = str(uuid.uuid4()) domain = user_email.split('@')[-1] if (domain,) not in masks: flask.flash('Not allowed email mask') else: row_mysql_queries.save_invitation( user_email, hash_code, form.role.data) invite_link = flask.url_for('.finish', invitation_hash=hash_code) msg = mail.Message('Invitation', recipients=[user_email]) msg.body = flask.render_template( 'invitations/email_body.txt', invite_link=invite_link) utils.send_msg(msg) flask.flash('Invitation sent successfully', 'info') return { 'form': form, 'masks': masks, 'title': bp.name.replace('_', ' ').capitalize(), 'subtitle': 'Send invitation' }
def invite(): """Send invitation. Admins are allowed to invite admins and members, members - only members. """ masks = row_mysql_queries.get_masks() form = forms.Invite() if form.validate_on_submit(): # Check if required conf setting exists _conf = flask.current_app.config if not 'KEYSTONE_CONF' in _conf or not 'NEO4J_API_URL' in _conf: raise Exception( """No required settings: KEYSTONE_CONF or NEO4J_API_URL, invitations wasn't sent""", "") user_email = form.email.data if not utils.username_is_taken(user_email): try: utils.neo4j_api_call('/users', {"email": user_email}, 'GET')[0] flask.flash( 'User with email "%s" is already registered' % user_email, 'error') except (KeyError, NotFound): # NOTE(apugachev) success, user does not exist hash_code = str(uuid.uuid4()) domain = user_email.split('@')[-1] if (domain, ) not in masks: flask.flash('Not allowed email mask') else: row_mysql_queries.save_invitation(user_email, hash_code, form.role.data) invite_link = flask.url_for('.finish', invitation_hash=hash_code) msg = mail.Message('Invitation', recipients=[user_email]) msg.body = flask.render_template( 'invitations/email_body.txt', invite_link=invite_link) utils.send_msg(msg) flask.flash('Invitation sent successfully', 'info') return { 'form': form, 'masks': masks, 'title': bp.name.replace('_', ' ').capitalize(), 'subtitle': 'Send invitation' }
def password_recovery_finish(recovery_hash): """ This will be called after user clicked link in email. """ try: id, email, hash_code, complete = \ row_mysql_queries.get_recovery_request_by_hash(recovery_hash) except TypeError: # db returns None flask.abort(404) if complete == 1: flask.flash('Password recovery token is expired', 'error') return flask.redirect(flask.url_for('dashboard')) odb_user = utils.neo4j_api_call('/users', {"email": email}, 'GET')[0] new_hash = str(uuid.uuid4()) # set trash password in keystone keystone_user = utils.get_keystone_user_by_username(odb_user['username']) clients.admin_clients().keystone.users.update_password( keystone_user, new_hash) # set trash password in odb utils.neo4j_api_call( '/users', { 'id': odb_user['id'], 'login': odb_user['login'], 'username': odb_user['username'], 'email': odb_user['email'], 'passwordHash': utils.create_hashed_password(new_hash) }, 'PUT') # send trash password back to user msg = mail.Message('Password recovery', recipients=[odb_user['email']]) msg.body = flask.render_template('RecoveryPasswordFinishEmail/body.txt', new_pass=new_hash) utils.send_msg(msg) flask.flash('New password was sent to you', 'success') return flask.redirect(flask.url_for('dashboard'))