def add_appointment_scheduler_to_dept(dept_code): _verify_membership_and_appointments_enabled(current_user, dept_code) params = request.get_json() or {} scheduler_uid = params.get('uid', None) if not scheduler_uid: raise errors.BadRequestError('Scheduler UID missing') calnet_user = calnet.get_calnet_user_for_uid(app, scheduler_uid, skip_expired_users=True) if not calnet_user or not calnet_user.get('csid'): raise errors.BadRequestError('Invalid scheduler UID') user = AuthorizedUser.create_or_restore( scheduler_uid, created_by=current_user.get_uid(), is_admin=False, is_blocked=False, can_access_canvas_data=False, ) Scheduler.create_or_update_membership( dept_code, user.id, drop_in=True, same_day=True, ) _create_department_memberships(user, [{ 'code': dept_code, 'role': 'scheduler', 'automateMembership': False }]) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify( _get_appointment_scheduler_list(current_user, dept_code))
def _update_drop_in_availability(uid, dept_code, new_availability): dept_code = dept_code.upper() if uid != current_user.get_uid(): authorized_to_toggle = current_user.is_admin or dept_code in [ d['code'] for d in current_user.departments if d.get('role') == 'scheduler' ] if not authorized_to_toggle: raise errors.ForbiddenRequestError( f'Unauthorized to toggle drop-in availability for department {dept_code}' ) drop_in_membership = None user = AuthorizedUser.find_by_uid(uid) if user: drop_in_membership = next( (d for d in user.drop_in_departments if d.dept_code == dept_code), None) if drop_in_membership: if drop_in_membership.is_available is True and new_availability is False: Appointment.unreserve_all_for_advisor(uid, current_user.get_id()) drop_in_membership.update_availability(new_availability) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify(drop_in_membership.to_api_json()) else: raise errors.ResourceNotFoundError( f'No drop-in advisor membership found: (uid={uid}, dept_code={dept_code})' )
def create_or_update_user_profile(): params = request.get_json() profile = params.get('profile', None) memberships = params.get('memberships', None) delete_action = to_bool_or_none(util.get(params, 'deleteAction')) if not profile or not profile.get('uid') or memberships is None: raise errors.BadRequestError('Required parameters are missing') authorized_user = _update_or_create_authorized_user(memberships, profile, include_deleted=True) _delete_existing_memberships(authorized_user) _create_department_memberships(authorized_user, memberships) if delete_action is True and not authorized_user.deleted_at: AuthorizedUser.delete(authorized_user.uid) elif delete_action is False and authorized_user.deleted_at: AuthorizedUser.un_delete(authorized_user.uid) user_id = authorized_user.id UserSession.flush_cache_for_id(user_id) updated_user = AuthorizedUser.find_by_id(user_id, include_deleted=True) users_json = authorized_users_api_feed([updated_user]) return tolerant_jsonify(users_json and users_json[0])
def _update_drop_in_status(uid, dept_code, active): dept_code = dept_code.upper() if uid == 'me': uid = current_user.get_uid() else: authorized_to_toggle = current_user.is_admin or dept_code in [ d['code'] for d in current_user.departments if d.get('isScheduler') ] if not authorized_to_toggle: raise errors.ForbiddenRequestError( f'Unauthorized to toggle drop-in status for department {dept_code}' ) drop_in_status = None user = AuthorizedUser.find_by_uid(uid) if user: drop_in_status = next( (d for d in user.drop_in_departments if d.dept_code == dept_code), None) if drop_in_status: drop_in_status.update_availability(active) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify(drop_in_status.to_api_json()) else: raise errors.ResourceNotFoundError( f'No drop-in advisor status found: (uid={uid}, dept_code={dept_code})' )
def enable_same_day_advising(dept_code): same_day_membership = SameDayAdvisor.create_or_update_membership( dept_code, current_user.user_id, is_available=True, ) UserSession.flush_cache_for_id(current_user.user_id) return tolerant_jsonify(same_day_membership.to_api_json())
def enable_drop_in_advising(dept_code): drop_in_membership = DropInAdvisor.create_or_update_membership( dept_code, current_user.user_id, is_available=False, ) UserSession.flush_cache_for_id(current_user.user_id) return tolerant_jsonify(drop_in_membership.to_api_json())
def _update_or_create_authorized_user(memberships, profile, include_deleted=False): user_id = profile.get('id') automate_degree_progress_permission = profile.get( 'automateDegreeProgressPermission') can_access_canvas_data = to_bool_or_none( profile.get('canAccessCanvasData')) can_access_advising_data = to_bool_or_none( profile.get('canAccessAdvisingData')) degree_progress_permission = profile.get('degreeProgressPermission') if (automate_degree_progress_permission or degree_progress_permission ) and 'COENG' not in dept_codes_where_advising( {'departments': memberships}): raise errors.BadRequestError( 'Degree Progress feature is only available to the College of Engineering.' ) is_admin = to_bool_or_none(profile.get('isAdmin')) is_blocked = to_bool_or_none(profile.get('isBlocked')) if user_id: user = AuthorizedUser.update_user( automate_degree_progress_permission= automate_degree_progress_permission, can_access_advising_data=can_access_advising_data, can_access_canvas_data=can_access_canvas_data, degree_progress_permission=degree_progress_permission, include_deleted=include_deleted, is_admin=is_admin, is_blocked=is_blocked, user_id=user_id, ) UserSession.flush_cache_for_id(user_id=user_id) return user else: uid = profile.get('uid') if AuthorizedUser.get_id_per_uid(uid, include_deleted=True): raise errors.BadRequestError( f'User with UID {uid} is already in the BOA database.') calnet_user = calnet.get_calnet_user_for_uid(app, uid, skip_expired_users=True) if calnet_user and calnet_user.get('csid', None): return AuthorizedUser.create_or_restore( automate_degree_progress_permission= automate_degree_progress_permission, can_access_advising_data=can_access_advising_data, can_access_canvas_data=can_access_canvas_data, created_by=current_user.get_uid(), degree_progress_permission=degree_progress_permission, is_admin=is_admin, is_blocked=is_blocked, uid=uid, ) else: raise errors.BadRequestError('Invalid UID')
def set_drop_in_advising_status(dept_code): user = AuthorizedUser.find_by_id(current_user.get_id()) drop_in_membership = next((d for d in user.drop_in_departments if d.dept_code == dept_code.upper()), None) if not drop_in_membership: raise errors.ResourceNotFoundError( f'No drop-in advisor membership found: (uid={current_user.get_uid()}, dept_code={dept_code})' ) params = request.get_json() if 'status' not in params: raise errors.BadRequestError('Missing status') if params['status'] and len(params['status']) > 255: raise errors.BadRequestError('Invalid status') drop_in_membership.update_status(params['status']) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify(drop_in_membership.to_api_json())
def _dev_auth_login(uid, password): if app.config['DEVELOPER_AUTH_ENABLED']: logger = app.logger if password != app.config['DEVELOPER_AUTH_PASSWORD']: logger.error('Dev-auth: Wrong password') return tolerant_jsonify({'message': 'Invalid credentials'}, 401) user_id = AuthorizedUser.get_id_per_uid(uid) if user_id is None: logger.error( f'Dev-auth: User with UID {uid} is not registered in BOA.') return tolerant_jsonify( { 'message': f'Sorry, user with UID {uid} is not registered to use BOA.' }, 403) user = UserSession(user_id=user_id, flush_cached=True) if not user.is_active: logger.error( f'Dev-auth: UID {uid} is registered with BOA but not active.') return tolerant_jsonify( { 'message': f'Sorry, user with UID {uid} is not authorized to use BOA.' }, 403) logger.info(f'Dev-auth used to log in as UID {uid}') login_user(user, force=True, remember=True) UserLogin.record_user_login(uid) return tolerant_jsonify(current_user.to_api_json()) else: raise ResourceNotFoundError('Unknown path')
def _create_department_memberships(authorized_user, memberships): for membership in [ m for m in memberships if m['role'] in ('advisor', 'director', 'scheduler') ]: university_dept = UniversityDept.find_by_dept_code(membership['code']) role = membership['role'] UniversityDeptMember.create_or_update_membership( university_dept_id=university_dept.id, authorized_user_id=authorized_user.id, role=role, automate_membership=to_bool_or_none( membership['automateMembership']), ) if role == 'scheduler': _delete_drop_in_advisor_status(authorized_user, university_dept.dept_code) UserSession.flush_cache_for_id(authorized_user.id)
def set_demo_mode(): if app.config['DEMO_MODE_AVAILABLE']: in_demo_mode = request.get_json().get('demoMode', None) if in_demo_mode is None: raise errors.BadRequestError('Parameter \'demoMode\' not found') user = AuthorizedUser.find_by_id(current_user.get_id()) user.in_demo_mode = bool(in_demo_mode) current_user.flush_cached() login_user(UserSession(user_id=user.id, flush_cached=True), force=True, remember=True) return tolerant_jsonify(current_user.to_api_json()) else: raise errors.ResourceNotFoundError('Unknown path')
def cas_login(): logger = app.logger ticket = request.args['ticket'] target_url = request.args.get('url') uid, attributes, proxy_granting_ticket = _cas_client( target_url).verify_ticket(ticket) logger.info(f'Logged into CAS as user {uid}') user_id = AuthorizedUser.get_id_per_uid(uid) if user_id is None: logger.error(f'UID {uid} is not an authorized user.') param = ('error', f""" Sorry, you are not registered to use BOA. Please <a href="mailto:{app.config['BOAC_SUPPORT_EMAIL']}">email us</a> for assistance. """) redirect_url = add_param_to_url('/', param) else: user = UserSession(user_id=user_id, flush_cached=True) if not user.is_active: logger.error( f'UID {uid} is in the BOA db but is not authorized to use the tool.' ) param = ('error', f""" Sorry, you are not registered to use BOA. Please <a href="mailto:{app.config['BOAC_SUPPORT_EMAIL']}">email us</a> for assistance. """) redirect_url = add_param_to_url('/', param) else: login_user(user) flash('Logged in successfully.') UserLogin.record_user_login(uid) # Check if url is safe for redirects per https://flask-login.readthedocs.io/en/latest/ if not _is_safe_url(request.args.get('next')): return abort(400) if not target_url: target_url = '/' # Our googleAnalyticsService uses 'casLogin' marker to track CAS login events redirect_url = add_param_to_url(target_url, ('casLogin', 'true')) return redirect(redirect_url)
def _user_loader(user_id=None, flush_cached=False): return UserSession(user_id, flush_cached)
def disable_same_day_advising(dept_code): user = AuthorizedUser.find_by_id(current_user.get_id()) SameDayAdvisor.delete(authorized_user_id=user.id, dept_code=dept_code) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify( {'message': 'Same-day advisor status has been disabled'}, status=200)
def disable_drop_in_advising(dept_code): user = AuthorizedUser.find_by_id(current_user.get_id()) _delete_drop_in_advisor_status(user, dept_code) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify( {'message': 'Drop-in advisor status has been disabled'}, status=200)