def auth_logout(auth, redirect_url=None, next_url=None): """ Log out, delete current session and remove OSF cookie. If next url is valid and auth is logged in, redirect to CAS logout endpoint with the current request url as service. If next url is valid and auth is logged out, redirect directly to the next url. Otherwise, redirect to CAS logout or login endpoint with redirect url as service. The CAS logout endpoint which clears sessions and cookies for CAS and Shibboleth. HTTP Method: GET Note 1: OSF tells CAS where it wants to be redirected back after successful logout. However, CAS logout flow may not respect this url if user is authenticated through remote identity provider. Note 2: The name of the query parameter is `next`, `next_url` is used to avoid python reserved word. :param auth: the authentication context :param redirect_url: url to DIRECTLY redirect after CAS logout, default is `OSF/goodbye` :param next_url: url to redirect after OSF logout, which is after CAS logout :return: the response """ # For `?next=`: # takes priority # the url must be a valid OSF next url, # the full request url is set to CAS service url, # does not support `reauth` # For `?redirect_url=`: # the url must be valid CAS service url # the redirect url is set to CAS service url. # support `reauth` # logout/?next=<an OSF verified next url> next_url = next_url or request.args.get('next', None) if next_url and validate_next_url(next_url): cas_logout_endpoint = cas.get_logout_url(request.url) if auth.logged_in: resp = redirect(cas_logout_endpoint) else: resp = redirect(next_url) # logout/ or logout/?redirect_url=<a CAS verified redirect url> else: redirect_url = redirect_url or request.args.get( 'redirect_url') or web_url_for('goodbye', _absolute=True) # set redirection to CAS log out (or log in if `reauth` is present) if 'reauth' in request.args: cas_endpoint = cas.get_login_url(redirect_url) else: cas_endpoint = cas.get_logout_url(redirect_url) resp = redirect(cas_endpoint) # perform OSF logout osf_logout() # set response to delete OSF cookie resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN) return resp
def auth_logout(auth, redirect_url=None, next_url=None): """ Log out, delete current session and remove OSF cookie. If next url is valid and auth is logged in, redirect to CAS logout endpoint with the current request url as service. If next url is valid and auth is logged out, redirect directly to the next url. Otherwise, redirect to CAS logout or login endpoint with redirect url as service. The CAS logout endpoint which clears sessions and cookies for CAS and Shibboleth. HTTP Method: GET Note 1: OSF tells CAS where it wants to be redirected back after successful logout. However, CAS logout flow may not respect this url if user is authenticated through remote identity provider. Note 2: The name of the query parameter is `next`, `next_url` is used to avoid python reserved word. :param auth: the authentication context :param redirect_url: url to DIRECTLY redirect after CAS logout, default is `OSF/goodbye` :param next_url: url to redirect after OSF logout, which is after CAS logout :return: the response """ # For `?next=`: # takes priority # the url must be a valid OSF next url, # the full request url is set to CAS service url, # does not support `reauth` # For `?redirect_url=`: # the url must be valid CAS service url # the redirect url is set to CAS service url. # support `reauth` # logout/?next=<an OSF verified next url> next_url = next_url or request.args.get('next', None) if next_url and validate_next_url(next_url): cas_logout_endpoint = cas.get_logout_url(request.url) if auth.logged_in: resp = redirect(cas_logout_endpoint) else: resp = redirect(next_url) # logout/ or logout/?redirect_url=<a CAS verified redirect url> else: redirect_url = redirect_url or request.args.get('redirect_url') or web_url_for('goodbye', _absolute=True) # set redirection to CAS log out (or log in if `reauth` is present) if 'reauth' in request.args: cas_endpoint = cas.get_login_url(redirect_url) else: cas_endpoint = cas.get_logout_url(redirect_url) resp = redirect(cas_endpoint) # perform OSF logout osf_logout() # set response to delete OSF cookie resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN) return resp
def auth_email_logout(token, user): """ When a user is adding an email or merging an account, add the email to the user and log them out. """ redirect_url = cas.get_logout_url(service_url=cas.get_login_url( service_url=web_url_for('index', _absolute=True))) try: unconfirmed_email = user.get_unconfirmed_email_for_token(token) except InvalidTokenError: raise HTTPError(http.BAD_REQUEST, data={ 'message_short': 'Bad token', 'message_long': 'The provided token is invalid.' }) except ExpiredTokenError: status.push_status_message('The private link you used is expired.') raise HTTPError(http.BAD_REQUEST, data={ 'message_short': 'Expired link', 'message_long': 'The private link you used is expired.' }) try: user_merge = OSFUser.objects.get(emails__address=unconfirmed_email) except OSFUser.DoesNotExist: user_merge = False if user_merge: remove_sessions_for_user(user_merge) user.email_verifications[token]['confirmed'] = True user.save() remove_sessions_for_user(user) resp = redirect(redirect_url) resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN) return resp
def auth_email_logout(token, user): """ When a user is adding an email or merging an account, add the email to the user and log them out. """ redirect_url = cas.get_logout_url(service_url=cas.get_login_url(service_url=web_url_for('index', _absolute=True))) try: unconfirmed_email = user.get_unconfirmed_email_for_token(token) except InvalidTokenError: raise HTTPError(http.BAD_REQUEST, data={ 'message_short': 'Bad token', 'message_long': 'The provided token is invalid.' }) except ExpiredTokenError: status.push_status_message('The private link you used is expired.') raise HTTPError(http.BAD_REQUEST, data={ 'message_short': 'Expired link', 'message_long': 'The private link you used is expired.' }) try: user_merge = User.find_one(Q('emails', 'eq', unconfirmed_email)) except NoResultsFound: user_merge = False if user_merge: remove_sessions_for_user(user_merge) user.email_verifications[token]['confirmed'] = True user.save() remove_sessions_for_user(user) resp = redirect(redirect_url) resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN) return resp
def auth_logout(redirect_url=None): """ Log out, delete current session and remove OSF cookie. Redirect to CAS logout which clears sessions and cookies for CAS and Shibboleth (if any). Final landing page may vary. HTTP Method: GET :param redirect_url: url to redirect user after CAS logout, default is 'goodbye' :return: """ # OSF tells CAS where it wants to be redirected back after successful logout. # However, CAS logout flow may not respect this url if user is authenticated through remote identity provider. redirect_url = redirect_url or request.args.get('redirect_url') or web_url_for('goodbye', _absolute=True) # OSF log out, remove current OSF session osf_logout() # set redirection to CAS log out (or log in if `reauth` is present) if 'reauth' in request.args: cas_endpoint = cas.get_login_url(redirect_url) else: cas_endpoint = cas.get_logout_url(redirect_url) resp = redirect(cas_endpoint) # delete OSF cookie resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN) return resp
def user_account_password(auth, **kwargs): user = auth.user old_password = request.form.get('old_password', None) new_password = request.form.get('new_password', None) confirm_password = request.form.get('confirm_password', None) # It has been more than 1 hour since last invalid attempt to change password. Reset the counter for invalid attempts. if throttle_period_expired(user.change_password_last_attempt, settings.TIME_RESET_CHANGE_PASSWORD_ATTEMPTS): user.reset_old_password_invalid_attempts() # There have been more than 3 failed attempts and throttle hasn't expired. if user.old_password_invalid_attempts >= settings.INCORRECT_PASSWORD_ATTEMPTS_ALLOWED and not throttle_period_expired(user.change_password_last_attempt, settings.CHANGE_PASSWORD_THROTTLE): push_status_message( message='Too many failed attempts. Please wait a while before attempting to change your password.', kind='warning', trust=False ) return redirect(web_url_for('user_account')) try: user.change_password(old_password, new_password, confirm_password) except ChangePasswordError as error: for m in error.messages: push_status_message(m, kind='warning', trust=False) else: # We have to logout the user first so all CAS sessions are invalid user.save() osf_logout() return redirect(cas.get_logout_url(cas.get_login_url( web_url_for('user_account', _absolute=True) + '?password_reset=True', username=user.username, verification_key=user.verification_key, ))) user.save() return redirect(web_url_for('user_account'))
def auth_logout(redirect_url=None): """Log out and delete cookie. """ redirect_url = redirect_url or request.args.get('redirect_url') logout() resp = redirect(cas.get_logout_url(redirect_url if redirect_url else web_url_for('goodbye', _absolute=True))) resp.delete_cookie(settings.COOKIE_NAME) return resp
def auth_logout(redirect_url=None): """Log out and delete cookie. """ redirect_url = redirect_url or request.args.get('redirect_url') or web_url_for('goodbye', _absolute=True) logout() if 'reauth' in request.args: cas_endpoint = cas.get_login_url(redirect_url) else: cas_endpoint = cas.get_logout_url(redirect_url) resp = redirect(cas_endpoint) resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN) return resp
def auth_logout(redirect_url=None, **kwargs): """ Log out, delete current session, delete CAS cookie and delete OSF cookie. HTTP Method: GET """ redirect_url = redirect_url or request.args.get('redirect_url') or web_url_for('goodbye', _absolute=True) # OSF log out, remove current OSF session logout() # set redirection to CAS log out (or log in if 'reauth' is present) if 'reauth' in request.args: cas_endpoint = cas.get_login_url(redirect_url) else: cas_endpoint = cas.get_logout_url(redirect_url) resp = redirect(cas_endpoint) # delete OSF cookie resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN) return resp
def claim_user_registered(auth, node, **kwargs): """ View that prompts user to enter their password in order to claim being a contributor on a project. A user must be logged in. """ current_user = auth.user sign_out_url = cas.get_logout_url(service_url=cas.get_login_url(service_url=request.url)) if not current_user: return redirect(sign_out_url) # Logged in user should not be a contributor the project if node.is_contributor(current_user): data = { 'message_short': 'Already a contributor', 'message_long': ('The logged-in user is already a contributor to this ' 'project. Would you like to <a href="{}">log out</a>?').format(sign_out_url) } raise HTTPError(http.BAD_REQUEST, data=data) uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token'] unreg_user = OSFUser.load(uid) if not verify_claim_token(unreg_user, token, pid=node._primary_key): error_data = { 'message_short': 'Invalid url.', 'message_long': 'The token in the URL is invalid or has expired.' } raise HTTPError(http.BAD_REQUEST, data=error_data) # Store the unreg_user data on the session in case the user registers # a new account session.data['unreg_user'] = { 'uid': uid, 'pid': pid, 'token': token } session.save() # If a user is already validated though external auth, it is OK to claim should_claim = check_external_auth(auth.user) form = PasswordForm(request.form) if request.method == 'POST': if form.validate(): if current_user.check_password(form.password.data): should_claim = True else: status.push_status_message(language.LOGIN_FAILED, kind='warning', trust=False) else: forms.push_errors_to_status(form.errors) if should_claim: node.replace_contributor(old=unreg_user, new=current_user) node.save() status.push_status_message( 'You are now a contributor to this project.', kind='success', trust=False ) return redirect(node.url) if is_json_request(): form_ret = forms.utils.jsonify(form) user_ret = profile_utils.serialize_user(current_user, full=False) else: form_ret = form user_ret = current_user return { 'form': form_ret, 'user': user_ret, 'signOutUrl': sign_out_url }
def claim_user_registered(auth, node, **kwargs): """ View that prompts user to enter their password in order to claim being a contributor on a project. A user must be logged in. """ current_user = auth.user sign_out_url = cas.get_logout_url(service_url=cas.get_login_url( service_url=request.url)) if not current_user: return redirect(sign_out_url) # Logged in user should not be a contributor the project if hasattr(node, 'is_contributor') and node.is_contributor(current_user): data = { 'message_short': 'Already a contributor', 'message_long': ('The logged-in user is already a contributor to this ' 'project. Would you like to <a href="{}">log out</a>?' ).format(sign_out_url) } raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data) # Logged in user is already a member of the OSF Group if hasattr(node, 'is_member') and node.is_member(current_user): data = { 'message_short': 'Already a member', 'message_long': ('The logged-in user is already a member of this OSF Group. ' 'Would you like to <a href="{}">log out</a>?' ).format(sign_out_url) } raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data) uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token'] unreg_user = OSFUser.load(uid) if not verify_claim_token(unreg_user, token, pid=node._primary_key): error_data = { 'message_short': 'Invalid url.', 'message_long': 'The token in the URL is invalid or has expired.' } raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data) # Store the unreg_user data on the session in case the user registers # a new account session.data['unreg_user'] = {'uid': uid, 'pid': pid, 'token': token} session.save() # If a user is already validated though external auth, it is OK to claim should_claim = check_external_auth(auth.user) form = PasswordForm(request.form) if request.method == 'POST': if form.validate(): if current_user.check_password(form.password.data): should_claim = True else: status.push_status_message(language.LOGIN_FAILED, kind='warning', trust=False) else: forms.push_errors_to_status(form.errors) if should_claim: node.replace_contributor(old=unreg_user, new=current_user) node.save() if isinstance(node, OSFGroup): status.push_status_message( 'You are now a member of this OSFGroup.', kind='success', trust=False) else: status.push_status_message( 'You are now a contributor to this project.', kind='success', trust=False) return redirect(node.url) if is_json_request(): form_ret = forms.utils.jsonify(form) user_ret = profile_utils.serialize_user(current_user, full=False) else: form_ret = form user_ret = current_user return {'form': form_ret, 'user': user_ret, 'signOutUrl': sign_out_url}