def api_verify_email(): """ @TODO: add counter/log to track failed attempts :rtype: Response :return the success or failed in json format """ if "POST" == request.method: token = utils.clean_str(request.form.get("tok")) else: token = utils.clean_str(request.args.get("tok")) if not token: return utils.jsonify_error({"message": "No token specified."}) try: email = utils.get_email_from_token(token, app.config["SECRET_KEY"], app.config["SECRET_KEY"]) except Exception as exc: # @TODO: add dedicated log type app.logger.error("api_verify_email: {}".format(exc.message)) return utils.jsonify_error({"message": exc.message}) app.logger.debug("Decoded email from token: {}".format(email)) user = UserEntity.query.filter_by(email=email).first() if user is None: app.logger.error("Attempt to verify email with incorrect token: {}".format(token)) return utils.jsonify_error({"message": "Sorry."}) user = UserEntity.update(user, email_confirmed_at=datetime.today()) app.logger.debug("Verified token {} for user {}".format(token, user.email)) # @TODO: add dedicated log type LogEntity.account_modified(session["uuid"], "Verified token {} for user {}".format(token, user.email)) return utils.jsonify_success({"message": "Email was verified for {}.".format(email)})
def api_deactivate_account(): """ De-activate an user. @TODO: should change expiration date too? :rtype: Response :return the success or failed in json format """ user_id = utils.get_safe_int(request.form.get("user_id")) user = UserEntity.get_by_id(user_id) user = UserEntity.update(user, active=False) LogEntity.account_modified(session["uuid"], "User deactivated: {}".format(user)) return utils.jsonify_success({"message": "User deactivated."})
def api_deactivate_account(): """ De-activate an user. @TODO: should change expiration date too? :rtype: Response :return the success or failed in json format """ user_id = utils.get_safe_int(request.form.get('user_id')) user = UserEntity.get_by_id(user_id) user = UserEntity.update(user, active=False) LogEntity.account_modified(session['uuid'], "User deactivated: {}".format(user)) return utils.jsonify_success({"message": "User deactivated."})
def api_extend_account(): """ Change the `User.usrAccessExpiresAt` to today's date + 180 days :rtype: Response :return the success or failed in json format """ user_id = request.form.get("user_id") today_plus_180 = utils.get_expiration_date(180) user = UserEntity.get_by_id(user_id) user = UserEntity.update(user, access_expires_at=today_plus_180) # @TODO: add dedicated log type LogEntity.account_modified(session["uuid"], "Updated expiration date to {}. {}".format(today_plus_180, user.email)) return utils.jsonify_success({"message": "Updated expiration date to {}".format(today_plus_180)})
def api_expire_account(): """ Change the `User.usrAccessExpiresAt` to today's date and 00:00:00 time effectively blocking the user access. :rtype: Response :return the success or failed in json format """ user_id = utils.get_safe_int(request.form.get("user_id")) user = UserEntity.get_by_id(user_id) today = datetime.today() today_start = datetime(today.year, today.month, today.day) user = UserEntity.update(user, access_expires_at=today_start) # @TODO: add dedicated log type LogEntity.account_modified(session["uuid"], "User access was expired. {}".format(user.email)) return utils.jsonify_success({"message": "User access was expired."})
def api_save_user(): """ Save a new user to the database """ email = request.form["email"] first = request.form["first"] last = request.form["last"] minitial = request.form["minitial"] roles = request.form.getlist("roles[]") email_exists = False try: existing_user = UserEntity.query.filter_by(email=email).one() email_exists = existing_user is not None except: pass if email_exists: return utils.jsonify_error({"message": "Sorry. This email is already taken."}) # @TODO: fix hardcoded values # salt, hashed_pass = generate_auth(app.config['SECRET_KEY'], password) added_date = datetime.today() access_end_date = utils.get_expiration_date(180) user = UserEntity.create( email=email, first=first, last=last, minitial=minitial, added_at=added_date, modified_at=added_date, access_expires_at=access_end_date, password_hash="", ) user_roles = [] try: for role_name in roles: role_entity = RoleEntity.query.filter_by(name=role_name).one() user_roles.append(role_entity) except Exception as exc: app.logger.debug("Problem saving user: {}".format(exc)) [user.roles.append(rol) for rol in user_roles] user = UserEntity.save(user) app.logger.debug("saved user: {}".format(user)) LogEntity.account_created(session["uuid"], user) return utils.jsonify_success({"user": user.serialize()})
def api_extend_account(): """ Change the `User.usrAccessExpiresAt` to today's date + 180 days :rtype: Response :return the success or failed in json format """ user_id = request.form.get('user_id') today_plus_180 = utils.get_expiration_date(180) user = UserEntity.get_by_id(user_id) user = UserEntity.update(user, access_expires_at=today_plus_180) # @TODO: add dedicated log type LogEntity.account_modified(session['uuid'], "Updated expiration date to {}. {}".format( today_plus_180, user.email)) return utils.jsonify_success( {"message": "Updated expiration date to {}".format(today_plus_180)})
def api_expire_account(): """ Change the `User.usrAccessExpiresAt` to today's date and 00:00:00 time effectively blocking the user access. :rtype: Response :return the success or failed in json format """ user_id = utils.get_safe_int(request.form.get('user_id')) user = UserEntity.get_by_id(user_id) today = datetime.today() today_start = datetime(today.year, today.month, today.day) user = UserEntity.update(user, access_expires_at=today_start) # @TODO: add dedicated log type LogEntity.account_modified(session['uuid'], "User access was expired. {}".format(user.email)) return utils.jsonify_success({"message": "User access was expired."})
def api_save_user(): """ Save a new user to the database """ email = request.form['email'] first = request.form['first'] last = request.form['last'] minitial = request.form['minitial'] roles = request.form.getlist('roles[]') email_exists = False try: existing_user = UserEntity.query.filter_by(email=email).one() email_exists = existing_user is not None except: pass if email_exists: return utils.jsonify_error( {'message': 'Sorry. This email is already taken.'}) # @TODO: fix hardcoded values # salt, hashed_pass = generate_auth(app.config['SECRET_KEY'], password) added_date = datetime.today() access_end_date = utils.get_expiration_date(180) user = UserEntity.create(email=email, first=first, last=last, minitial=minitial, added_at=added_date, modified_at=added_date, access_expires_at=access_end_date, password_hash="") user_roles = [] try: for role_name in roles: role_entity = RoleEntity.query.filter_by(name=role_name).one() user_roles.append(role_entity) except Exception as exc: app.logger.debug("Problem saving user: {}".format(exc)) [user.roles.append(rol) for rol in user_roles] user = UserEntity.save(user) app.logger.debug("saved user: {}".format(user)) LogEntity.account_created(session['uuid'], user) return utils.jsonify_success({'user': user.serialize()})
def test_log_creation(self): """ Test CRUD operations """ # LogType log_login = LogTypeEntity.create(type=LOG_TYPE_LOGIN, description='') log_logout = LogTypeEntity.create(type=LOG_TYPE_LOGOUT, description='') self.assertEquals(1, log_login.id) self.assertEquals(2, log_logout.id) # UserAgent user_agent_string = "Long text..." hash = compute_text_md5(user_agent_string) user_agent = UserAgentEntity.create(user_agent=user_agent_string, hash=hash, platform="Linux", browser="Firefox", version="latest", language="EN-US") # print(user_agent) self.assertEquals(1, user_agent.id) self.assertEquals("467ffa17419afeffe09bb98af4828a30", user_agent.hash) self.assertEquals("Linux", user_agent.platform) self.assertEquals("latest", user_agent.version) self.assertEquals("EN-US", user_agent.language) # WebSession web_session = WebSessionEntity.create(user_agent_id=user_agent.id) self.assertEquals(1, web_session.id) self.assertEquals(user_agent, web_session.user_agent) # print(web_session.user_agent) # Log log = LogEntity.create(type_id=log_login.id, web_session_id=web_session.id, date_time=datetime.now(), details='just a test') log2 = LogEntity.create(type_id=log_logout.id, web_session_id=web_session.id, date_time=datetime.now(), details='just a test') self.assertEquals(1, log.id) self.assertEquals(2, log2.id) self.assertEquals(LOG_TYPE_LOGIN, log.log_type.type) self.assertEquals(LOG_TYPE_LOGOUT, log2.log_type.type)
def shibb_return(): """ Read the Shibboleth headers returned by the IdP after the user entered the username/password. If the `eduPersonPrincipalName` (aka Eppn) for the user matches the usrEmail of an active user then let the user in, otherwise let them see the login page. @see #shibb_redirect() """ if current_user.is_authenticated(): # next_page = request.args.get('next') or get_role_landing_page() return redirect(get_role_landing_page()) # fresh login... uuid = session['uuid'] email = request.headers['Mail'] glid = request.headers['Glid'] # Gatorlink ID app.logger.debug("Checking if email: {} is registered for glid: {}" .format(email, glid)) user = UserEntity.query.filter_by(email=email).first() if not user: utils.flash_error("No such user: {}".format(email)) LogEntity.login_error(uuid, "Shibboleth user is not registered for this app") return redirect(url_for('index')) if not user.is_active(): utils.flash_error("Inactive user: {}".format(email)) LogEntity.login_error(uuid, 'Inactive user tried to login') return redirect(url_for('index')) if user.is_expired(): utils.flash_error("User account for {} expired on {}" .format(email, user.access_expires_at)) LogEntity.login_error(uuid, 'Expired user tried to login') return redirect(url_for('index')) # Log it app.logger.info('Successful login via Shibboleth for: {}'.format(user)) LogEntity.login(uuid, 'Successful login via Shibboleth') login_user(user, remember=False, force=False) # Tell Flask-Principal that the identity has changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.get_id())) next_page = get_role_landing_page() return redirect(next_page)
def api_verify_email(): """ @TODO: add counter/log to track failed attempts :rtype: Response :return the success or failed in json format """ if 'POST' == request.method: token = utils.clean_str(request.form.get('tok')) else: token = utils.clean_str(request.args.get('tok')) if not token: return utils.jsonify_error({'message': 'No token specified.'}) try: email = utils.get_email_from_token(token, app.config["SECRET_KEY"], app.config["SECRET_KEY"]) except Exception as exc: # @TODO: add dedicated log type app.logger.error("api_verify_email: {}".format(exc.message)) return utils.jsonify_error({'message': exc.message}) app.logger.debug("Decoded email from token: {}".format(email)) user = UserEntity.query.filter_by(email=email).first() if user is None: app.logger.error("Attempt to verify email with incorrect token: {}" .format(token)) return utils.jsonify_error({'message': 'Sorry.'}) user = UserEntity.update(user, email_confirmed_at=datetime.today()) app.logger.debug("Verified token {} for user {}".format(token, user.email)) # @TODO: add dedicated log type LogEntity.account_modified(session['uuid'], "Verified token {} for user {}".format( token, user.email)) return utils.jsonify_success( {"message": "Email was verified for {}.".format(email)})
def logout(): """ Destroy the user session and redirect to the home page Shib: https://shib.ncsu.edu/docs/logout.html https://wiki.shibboleth.net/confluence/display/CONCEPT/SLOIssues """ # Log the logout if 'uuid' in session: LogEntity.logout(session['uuid']) logout_user() # Remove session keys set by Flask-Principal, and `uuid` key set manually for key in ('identity.name', 'identity.auth_type', 'uuid'): session.pop(key, None) # Tell Flask-Principal the user is anonymous identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity()) return redirect('/')
def logout(): """ Destroy the user session and redirect to the home page Shib: https://shib.ncsu.edu/docs/logout.html https://wiki.shibboleth.net/confluence/display/CONCEPT/SLOIssues """ logout_user() # Remove session keys set by Flask-Principal for key in ('identity.name', 'identity.auth_type'): session.pop(key, None) # Tell Flask-Principal the user is anonymous identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity()) # Log the logout LogEntity.logout(session['uuid']) # Also pop the session id session.pop('uuid') return redirect('/')
def api_list_logs(): """ Render the specified page of event logs @TODO: show user-specific logs for non-admins? :rtype: string :return the json list of logs """ if "POST" == request.method: per_page = utils.get_safe_int(request.form.get("per_page")) page_num = utils.get_safe_int(request.form.get("page_num")) else: per_page = utils.get_safe_int(request.args.get("per_page")) page_num = utils.get_safe_int(request.args.get("page_num")) logs, total_pages = LogEntity.get_logs(per_page, page_num) return utils.jsonify_success(dict(list_of_events=logs, total_pages=total_pages))
def api_list_logs(): """ Render the specified page of event logs @TODO: show user-specific logs for non-admins? :rtype: string :return the json list of logs """ if 'POST' == request.method: per_page = utils.get_safe_int(request.form.get('per_page')) page_num = utils.get_safe_int(request.form.get('page_num')) else: per_page = utils.get_safe_int(request.args.get('per_page')) page_num = utils.get_safe_int(request.args.get('page_num')) logs, total_pages = LogEntity.get_logs(per_page, page_num) return utils.jsonify_success( dict(list_of_events=logs, total_pages=total_pages))
def render_login_local(): """ Render the login page with username/pass @see #index() @see #render_login_shib() """ if current_user.is_authenticated(): return redirect(get_role_landing_page()) uuid = session['uuid'] form = LoginForm(request.form) if request.method == 'POST' and form.validate(): email = form.email.data.strip( ) if form.email.data else "" password = form.password.data.strip() if form.password.data else "" app.logger.debug("{} password: {}".format(email, password)) app.logger.debug("Checking email: {}".format(email)) user = UserEntity.query.filter_by(email=email).first() if user: app.logger.debug("Found user object: {}".format(user)) else: utils.flash_error("No such email: {}".format(email)) LogEntity.login(uuid, "No such email: {}".format(email)) return redirect(url_for('index')) # if utils.is_valid_auth(app.config['SECRET_KEY'], auth.uathSalt, # password, auth.uathPassword): if '' == user.password_hash: app.logger.info('Log login event for: {}'.format(user)) LogEntity.login(uuid, 'Successful login via email/password') login_user(user, remember=False, force=False) # Tell Flask-Principal that the identity has changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.get_id())) return redirect(get_role_landing_page()) else: app.logger.info('Incorrect pass for: {}'.format(user)) LogEntity.login_error(uuid, 'Incorrect pass for: {}'.format(user)) # When sending a GET request render the login form return render_template('index.html', form=form, next_page=request.args.get('next'))
def render_login_local(): """ Render the login page with username/pass @see #index() @see #render_login_shib() """ if current_user.is_authenticated(): return redirect(get_role_landing_page()) uuid = session['uuid'] form = LoginForm(request.form) if request.method == 'POST' and form.validate(): email = form.email.data.strip( ) if form.email.data else "*****@*****.**" password = form.password.data.strip() if form.password.data else "" app.logger.debug("{} password: {}".format(email, password)) app.logger.debug("Checking email: {}".format(email)) user = UserEntity.query.filter_by(email=email).first() if user: app.logger.debug("Found user object: {}".format(user)) else: utils.flash_error("No such email: {}".format(email)) LogEntity.login(uuid, "No such email: {}".format(email)) return redirect(url_for('index')) # if utils.is_valid_auth(app.config['SECRET_KEY'], auth.uathSalt, # password, auth.uathPassword): if '' == user.password_hash: app.logger.info('Log login event for: {}'.format(user)) LogEntity.login(uuid, 'Successful login via email/password') login_user(user, remember=False, force=False) # Tell Flask-Principal that the identity has changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.get_id())) return redirect(get_role_landing_page()) else: app.logger.info('Incorrect pass for: {}'.format(user)) LogEntity.login_error(uuid, 'Incorrect pass for: {}'.format(user)) # When sending a GET request render the login form return render_template('index.html', form=form, next_page=request.args.get('next'))