def claim_user_post(node, **kwargs): """View for claiming a user from the X-editable form on a project page. """ reqdata = request.json # Unreg user user = User.load(reqdata['pk']) unclaimed_data = user.get_unclaimed_record(node._primary_key) # Submitted through X-editable if 'value' in reqdata: # Submitted email address email = reqdata['value'].lower().strip() claimer = get_user(email=email) if claimer and claimer.is_registered: send_claim_registered_email(claimer=claimer, unreg_user=user, node=node) else: send_claim_email(email, user, node, notify=True) # TODO(sloria): Too many assumptions about the request data. Just use elif 'claimerId' in reqdata: # User is logged in and confirmed identity claimer_id = reqdata['claimerId'] claimer = User.load(claimer_id) send_claim_registered_email(claimer=claimer, unreg_user=user, node=node) email = claimer.username else: raise HTTPError(http.BAD_REQUEST) return { 'status': 'success', 'email': email, 'fullname': unclaimed_data['name'] }
def get_most_in_common_contributors(auth, node, **kwargs): node_contrib_ids = set(node.contributors._to_primary_keys()) try: n_contribs = int(request.args.get('max', None)) except (TypeError, ValueError): n_contribs = settings.MAX_MOST_IN_COMMON_LENGTH contrib_counts = Counter(contrib_id for node in auth.user.node__contributed for contrib_id in node.contributors._to_primary_keys() if contrib_id not in node_contrib_ids) active_contribs = itertools.ifilter( lambda c: User.load(c[0]).is_active, contrib_counts.most_common() ) limited = itertools.islice(active_contribs, n_contribs) contrib_objs = [(User.load(_id), count) for _id, count in limited] contribs = [ utils.add_contributor_json(most_contrib, auth.user) for most_contrib, count in sorted(contrib_objs, key=lambda t: (-t[1], t[0].fullname)) ] return {'contributors': contribs}
def claim_user_post(node, **kwargs): """ View for claiming a user from the X-editable form on a project page. :param node: the project node :return: """ request_data = request.json # The unclaimed user unclaimed_user = User.load(request_data["pk"]) unclaimed_data = unclaimed_user.get_unclaimed_record(node._primary_key) # Claimer is not logged in and submit her/his email through X-editable, stored in `request_data['value']` if "value" in request_data: email = request_data["value"].lower().strip() claimer = get_user(email=email) # registered user if claimer and claimer.is_registered: send_claim_registered_email(claimer, unclaimed_user, node) # unregistered user else: send_claim_email(email, unclaimed_user, node, notify=True) # Claimer is logged in with confirmed identity stored in `request_data['claimerId']` elif "claimerId" in request_data: claimer_id = request_data["claimerId"] claimer = User.load(claimer_id) send_claim_registered_email(claimer, unclaimed_user, node) email = claimer.username else: raise HTTPError(http.BAD_REQUEST) return {"status": "success", "email": email, "fullname": unclaimed_data["name"]}
def project_removecontributor(auth, node, **kwargs): contributor = User.load(request.json['id']) if contributor is None: raise HTTPError(http.BAD_REQUEST) # Forbidden unless user is removing herself if not node.has_permission(auth.user, 'admin'): if auth.user != contributor: raise HTTPError(http.FORBIDDEN) outcome = node.remove_contributor( contributor=contributor, auth=auth, ) if outcome: if auth.user == contributor: status.push_status_message('Removed self from project', 'info') return {'redirectUrl': web_url_for('dashboard')} status.push_status_message('Contributor removed', 'info') return {} raise HTTPError( http.BAD_REQUEST, data={ 'message_long': ( '{0} must have at least one contributor with admin ' 'rights'.format( node.project_or_component.capitalize() ) ) } )
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600): unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unreg_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) timestamp = unclaimed_record.get('last_sent') if throttle_period_expired(timestamp, throttle): # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGiSTERED, user=unreg_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unreg_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def get_user_from_cas_resp(cas_resp): """ Given a CAS service validation response, attempt to retrieve user information and next action. :param cas_resp: the cas service validation response :return: the user, the external_credential, and the next action """ if cas_resp.user: user = User.load(cas_resp.user) # cas returns a valid OSF user id if user: return user, None, 'authenticate' # cas does not return a valid OSF user id else: external_credential = validate_external_credential(cas_resp.user) # invalid cas response if not external_credential: return None, None, None # cas returns a valid external credential user = get_user(external_id_provider=external_credential['provider'], external_id=external_credential['id']) # existing user found if user: return user, external_credential, 'authenticate' # user first time login through external identity provider else: return None, external_credential, 'external_first_login'
def get_node_contributors_abbrev(auth, node, **kwargs): anonymous = has_anonymous_link(node, auth) formatter = "surname" max_count = kwargs.get("max_count", 3) if "user_ids" in kwargs: users = [User.load(user_id) for user_id in kwargs["user_ids"] if user_id in node.visible_contributor_ids] else: users = node.visible_contributors if anonymous or not node.can_view(auth): raise HTTPError(http.FORBIDDEN) contributors = [] n_contributors = len(users) others_count = "" for index, user in enumerate(users[:max_count]): if index == max_count - 1 and len(users) > max_count: separator = " &" others_count = str(n_contributors - 3) elif index == len(users) - 1: separator = "" elif index == len(users) - 2: separator = " &" else: separator = "," contributor = user.get_summary(formatter) contributor["user_id"] = user._primary_key contributor["separator"] = separator contributors.append(contributor) return {"contributors": contributors, "others_count": others_count}
def project_before_remove_contributor(auth, node, **kwargs): contributor = User.load(request.json.get('id')) # Forbidden unless user is removing herself if not node.has_permission(auth.user, 'admin'): if auth.user != contributor: raise HTTPError(http.FORBIDDEN) if len(node.visible_contributor_ids) == 1 \ and node.visible_contributor_ids[0] == contributor._id: raise HTTPError(http.FORBIDDEN, data={ 'message_long': 'Must have at least one bibliographic contributor' }) prompts = node.callback( 'before_remove_contributor', removed=contributor, ) if auth.user == contributor: prompts.insert( 0, 'Are you sure you want to remove yourself from this project?' ) return {'prompts': prompts}
def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 3600): """ A registered user claiming the unclaimed user account as an contributor to a project. Send an email for claiming the account to the referrer and notify the claimer. :param claimer: the claimer :param unclaimed_user: the user account to claim :param node: the project node where the user account is claimed :param throttle: the time period in seconds before another claim for the account can be made :return: :raise: http.BAD_REQUEST """ unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(http.BAD_REQUEST, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer.username unclaimed_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unclaimed_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGISTERED, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unclaimed_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600): """Send an email for claiming a user account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. """ claimer_email = email.lower().strip() unclaimed_record = user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = user.get_claim_url(node._primary_key, external=True) # If given email is the same provided by user, just send to that email if unclaimed_record.get('email') == claimer_email: mail_tpl = mails.INVITE to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email user.save() else: # Otherwise have the referrer forward the email to the user # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(400, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer_email user.save() claim_url = user.get_claim_url(node._primary_key, external=True) if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail( claimer_email, pending_mail, user=user, referrer=referrer, fullname=unclaimed_record['name'], node=node ) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username mails.send_mail( to_addr, mail_tpl, user=user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name'] ) return to_addr
def claim_user_registered(auth, node, **kwargs): """View that prompts user to enter their password in order to claim contributorship on a project. A user must be logged in. """ current_user = auth.user sign_out_url = web_url_for('auth_login', logout=True, next=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): logout_url = web_url_for('auth_logout', redirect_url=request.url) 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(logout_url) } raise HTTPError(http.BAD_REQUEST, data=data) uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token'] unreg_user = User.load(uid) if not verify_claim_token(unreg_user, token, pid=node._primary_key): raise HTTPError(http.BAD_REQUEST) # 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 } form = PasswordForm(request.form) if request.method == 'POST': if form.validate(): if current_user.check_password(form.password.data): 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) else: status.push_status_message(language.LOGIN_FAILED, kind='warning', trust=False) else: forms.push_errors_to_status(form.errors) 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_form(auth, **kwargs): """ View for rendering the set password page for a claimed user. Must have ``token`` as a querystring argument. Renders the set password form, validates it, and sets the user's password. HTTP Method: GET, POST """ uid, pid = kwargs['uid'], kwargs['pid'] token = request.form.get('token') or request.args.get('token') user = User.load(uid) # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error if not user or not verify_claim_token(user, token, pid): error_data = { 'message_short': 'Invalid url.', 'message_long': 'Claim user does not exists, the token in the URL is invalid or has expired.' } raise HTTPError(http.BAD_REQUEST, data=error_data) # If user is logged in, redirect to 're-enter password' page if auth.logged_in: return redirect(web_url_for('claim_user_registered', uid=uid, pid=pid, token=token)) unclaimed_record = user.unclaimed_records[pid] user.fullname = unclaimed_record['name'] user.update_guessed_names() # The email can be the original referrer email if no claimer email has been specified. claimer_email = unclaimed_record.get('claimer_email') or unclaimed_record.get('email') form = SetEmailAndPasswordForm(request.form, token=token) if request.method == 'POST': if form.validate(): username, password = claimer_email, form.password.data user.register(username=username, password=password) # Clear unclaimed records user.unclaimed_records = {} user.verification_key = generate_verification_key() user.save() # Authenticate user and redirect to project page status.push_status_message(language.CLAIMED_CONTRIBUTOR, kind='success', trust=True) # Redirect to CAS and authenticate the user with a verification key. return redirect(cas.get_login_url( web_url_for('view_project', pid=pid, _absolute=True), username=user.username, verification_key=user.verification_key )) else: forms.push_errors_to_status(form.errors) return { 'firstname': user.given_name, 'email': claimer_email if claimer_email else '', 'fullname': user.fullname, 'form': forms.utils.jsonify(form) if is_json_request() else form, }
def claim_user_form(auth, **kwargs): """View for rendering the set password page for a claimed user. Must have ``token`` as a querystring argument. Renders the set password form, validates it, and sets the user's password. """ uid, pid = kwargs['uid'], kwargs['pid'] token = request.form.get('token') or request.args.get('token') # If user is logged in, redirect to 're-enter password' page if auth.logged_in: return redirect(web_url_for('claim_user_registered', uid=uid, pid=pid, token=token)) user = User.load(uid) # The unregistered user # user ID is invalid. Unregistered user is not in database if not user: raise HTTPError(http.BAD_REQUEST) # If claim token not valid, redirect to registration page if not verify_claim_token(user, token, pid): return redirect(web_url_for('auth_login')) unclaimed_record = user.unclaimed_records[pid] user.fullname = unclaimed_record['name'] user.update_guessed_names() # The email can be the original referrer email if no claimer email has been specified. claimer_email = unclaimed_record.get('claimer_email') or unclaimed_record.get('email') form = SetEmailAndPasswordForm(request.form, token=token) if request.method == 'POST': if form.validate(): username, password = claimer_email, form.password.data user.register(username=username, password=password) # Clear unclaimed records user.unclaimed_records = {} user.verification_key = security.random_string(20) user.save() # Authenticate user and redirect to project page node = Node.load(pid) status.push_status_message(language.CLAIMED_CONTRIBUTOR.format(node=node), kind='success', trust=True) # Redirect to CAS and authenticate the user with a verification key. return redirect(cas.get_login_url( web_url_for('user_profile', _absolute=True), auto=True, username=user.username, verification_key=user.verification_key )) else: forms.push_errors_to_status(form.errors) return { 'firstname': user.given_name, 'email': claimer_email if claimer_email else '', 'fullname': user.fullname, 'form': forms.utils.jsonify(form) if is_json_request() else form, }
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 = web_url_for("auth_register", logout=True, next=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): logout_url = web_url_for("auth_logout", redirect_url=request.url) 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(logout_url), } raise HTTPError(http.BAD_REQUEST, data=data) uid, pid, token = kwargs["uid"], kwargs["pid"], kwargs["token"] unreg_user = User.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} form = PasswordForm(request.form) if request.method == "POST": if form.validate(): if current_user.check_password(form.password.data): 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) else: status.push_status_message(language.LOGIN_FAILED, kind="warning", trust=False) else: forms.push_errors_to_status(form.errors) 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 deserialize_contributors(node, user_dicts, auth, validate=False): """View helper that returns a list of User objects from a list of serialized users (dicts). The users in the list may be registered or unregistered users. e.g. ``[{'id': 'abc123', 'registered': True, 'fullname': ..}, {'id': None, 'registered': False, 'fullname'...}, {'id': '123ab', 'registered': False, 'fullname': ...}] If a dict represents an unregistered user without an ID, creates a new unregistered User record. :param Node node: The node to add contributors to :param list(dict) user_dicts: List of serialized users in the format above. :param Auth auth: :param bool validate: Whether to validate and sanitize fields (if necessary) """ # Add the registered contributors contribs = [] for contrib_dict in user_dicts: fullname = contrib_dict["fullname"] visible = contrib_dict["visible"] email = contrib_dict.get("email") if validate is True: # Validate and sanitize inputs as needed. Email will raise error if invalid. # TODO Edge case bug: validation and saving are performed in same loop, so all in list # up to the invalid entry will be saved. (communicate to the user what needs to be retried) fullname = sanitize.strip_html(fullname) if not fullname: raise ValidationValueError("Full name field cannot be empty") if email: validate_email(email) # Will raise a ValidationError if email invalid if contrib_dict["id"]: contributor = User.load(contrib_dict["id"]) else: try: contributor = User.create_unregistered(fullname=fullname, email=email) contributor.save() except ValidationValueError: ## FIXME: This suppresses an exception if ID not found & new validation fails; get_user will return None contributor = get_user(email=email) # Add unclaimed record if necessary if not contributor.is_registered and node._primary_key not in contributor.unclaimed_records: contributor.add_unclaimed_record(node=node, referrer=auth.user, given_name=fullname, email=email) contributor.save() contribs.append( {"user": contributor, "visible": visible, "permissions": expand_permissions(contrib_dict.get("permission"))} ) return contribs
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600): """Send an email for claiming a user account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. """ invited_email = email.lower().strip() unclaimed_record = user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = user.get_claim_url(node._primary_key, external=True) # If given email is the same provided by user, just send to that email if unclaimed_record.get('email', None) == invited_email: mail_tpl = mails.INVITE to_addr = invited_email else: # Otherwise have the referrer forward the email to the user if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail( invited_email, pending_mail, user=user, referrer=referrer, fullname=unclaimed_record['name'], node=node ) timestamp = unclaimed_record.get('last_sent') if throttle_period_expired(timestamp, throttle): unclaimed_record['last_sent'] = get_timestamp() user.save() else: # Don't send the email to the referrer return mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username mails.send_mail( to_addr, mail_tpl, user=user, referrer=referrer, node=node, claim_url=claim_url, email=invited_email, fullname=unclaimed_record['name'] ) return to_addr
def find_inactive_users_with_no_inactivity_email_sent_or_queued(): inactive_users = User.find( (Q('date_last_login', 'lt', datetime.utcnow() - settings.NO_LOGIN_WAIT_TIME) & Q('osf4m', 'ne', 'system_tags')) | (Q('date_last_login', 'lt', datetime.utcnow() - settings.NO_LOGIN_OSF4M_WAIT_TIME) & Q('osf4m', 'eq', 'system_tags')) ) inactive_emails = mails.QueuedMail.find(Q('email_type', 'eq', mails.NO_LOGIN_TYPE)) #This is done to prevent User query returns comparison to User, as equality fails #on datetime fields due to pymongo rounding. Instead here _id is compared. users_sent_id = [email.user._id for email in inactive_emails] inactive_ids = [user._id for user in inactive_users if user.is_active] users_to_send = [User.load(id) for id in (set(inactive_ids) - set(users_sent_id))] return users_to_send
def deserialize_contributors(node, user_dicts, auth): """View helper that returns a list of User objects from a list of serialized users (dicts). The users in the list may be registered or unregistered users. e.g. ``[{'id': 'abc123', 'registered': True, 'fullname': ..}, {'id': None, 'registered': False, 'fullname'...}, {'id': '123ab', 'registered': False, 'fullname': ...}] If a dict represents an unregistered user without an ID, creates a new unregistered User record. :param Node node: The node to add contributors to :param list(dict) user_dicts: List of serialized users in the format above. :param Auth auth: """ # Add the registered contributors contribs = [] for contrib_dict in user_dicts: fullname = contrib_dict['fullname'] visible = contrib_dict['visible'] email = contrib_dict.get('email') if contrib_dict['id']: contributor = User.load(contrib_dict['id']) else: try: contributor = User.create_unregistered( fullname=fullname, email=email) contributor.save() except ValidationValueError: contributor = get_user(email=email) # Add unclaimed record if necessary if (not contributor.is_registered and node._primary_key not in contributor.unclaimed_records): contributor.add_unclaimed_record(node=node, referrer=auth.user, given_name=fullname, email=email) contributor.save() unreg_contributor_added.send(node, contributor=contributor, auth=auth) contribs.append({ 'user': contributor, 'visible': visible, 'permissions': expand_permissions(contrib_dict.get('permission')) }) return contribs
def project_remove_contributor(auth, **kwargs): """Remove a contributor from a list of nodes. :param Auth auth: Consolidated authorization :raises: HTTPError(400) if contributors to be removed are not in list or if no admin users would remain after changes were applied """ contributor_id = request.get_json()['contributorID'] node_ids = request.get_json()['nodeIDs'] contributor = User.load(contributor_id) if contributor is None: raise HTTPError(http.BAD_REQUEST, data={'message_long': 'Contributor not found.'}) redirect_url = {} parent_id = node_ids[0] for node_id in node_ids: # Update permissions and order node = Node.load(node_id) # Forbidden unless user is removing herself if not node.has_permission(auth.user, 'admin'): if auth.user != contributor: raise HTTPError(http.FORBIDDEN) if len(node.visible_contributor_ids) == 1 \ and node.visible_contributor_ids[0] == contributor._id: raise HTTPError(http.FORBIDDEN, data={ 'message_long': 'Must have at least one bibliographic contributor' }) nodes_removed = node.remove_contributor(contributor, auth=auth) # remove_contributor returns false if there is not one admin or visible contributor left after the move. if not nodes_removed: raise HTTPError(http.BAD_REQUEST, data={ 'message_long': 'Could not remove contributor.'}) # On parent node, if user has removed herself from project, alert; redirect to user # dashboard if node is private, else node dashboard if not node.is_contributor(auth.user) and node_id == parent_id: status.push_status_message( 'You have removed yourself as a contributor from this project', kind='success', trust=False ) if node.is_public: redirect_url = {'redirectUrl': node.url} # Else stay on current page else: redirect_url = {'redirectUrl': web_url_for('dashboard')} return redirect_url
def replace_unclaimed_user_with_registered(user): """Listens for the user_registered signal. If unreg_user is stored in the session, then the current user is trying to claim themselves as a contributor. Replaces the old, unregistered contributor with the newly registered account. """ unreg_user_info = session.data.get("unreg_user") if unreg_user_info: unreg_user = User.load(unreg_user_info["uid"]) pid = unreg_user_info["pid"] node = Node.load(pid) node.replace_contributor(old=unreg_user, new=user) node.save() status.push_status_message("Successfully claimed contributor.", kind="success", trust=False)
def claim_user_form(auth, **kwargs): """View for rendering the set password page for a claimed user. Must have ``token`` as a querystring argument. Renders the set password form, validates it, and sets the user's password. """ uid, pid = kwargs['uid'], kwargs['pid'] token = request.form.get('token') or request.args.get('token') # If user is logged in, redirect to 're-enter password' page if auth.logged_in: return redirect(web_url_for('claim_user_registered', uid=uid, pid=pid, token=token)) user = User.load(uid) # The unregistered user # user ID is invalid. Unregistered user is not in database if not user: raise HTTPError(http.BAD_REQUEST) # If claim token not valid, redirect to registration page if not verify_claim_token(user, token, pid): return redirect('/account/') unclaimed_record = user.unclaimed_records[pid] user.fullname = unclaimed_record['name'] user.update_guessed_names() email = unclaimed_record['email'] form = SetEmailAndPasswordForm(request.form, token=token) if request.method == 'POST': if form.validate(): username, password = email, form.password.data user.register(username=username, password=password) # Clear unclaimed records user.unclaimed_records = {} user.save() # Authenticate user and redirect to project page response = redirect('/settings/') node = Node.load(pid) status.push_status_message(language.CLAIMED_CONTRIBUTOR.format(node=node), 'success') return authenticate(user, response) else: forms.push_errors_to_status(form.errors) return { 'firstname': user.given_name, 'email': email if email else '', 'fullname': user.fullname, 'form': forms.utils.jsonify(form) if is_json_request() else form, }
def make_response_from_ticket(ticket, service_url): """Given a CAS ticket and service URL, attempt to the user and return a proper redirect response. """ service_furl = furl.furl(service_url) if 'ticket' in service_furl.args: service_furl.args.pop('ticket') client = get_client() cas_resp = client.service_validate(ticket, service_furl.url) if cas_resp.authenticated: user = User.load(cas_resp.user) # if we successfully authenticate and a verification key is present, invalidate it if user.verification_key: user.verification_key = None user.save() return authenticate(user, access_token=cas_resp.attributes['accessToken'], response=redirect(service_furl.url)) # Ticket could not be validated, unauthorized. return redirect(service_furl.url)
def get_node_contributors_abbrev(auth, **kwargs): node = kwargs['node'] or kwargs['project'] anonymous = has_anonymous_link(node, auth) max_count = kwargs.get('max_count', 3) if 'user_ids' in kwargs: users = [ User.load(user_id) for user_id in kwargs['user_ids'] if user_id in node.visible_contributor_ids ] else: users = node.visible_contributors if anonymous or not node.can_view(auth): raise HTTPError(http.FORBIDDEN) contributors = [] n_contributors = len(users) others_count = '' for index, user in enumerate(users[:max_count]): if index == max_count - 1 and len(users) > max_count: separator = ' &' others_count = str(n_contributors - 3) elif index == len(users) - 1: separator = '' elif index == len(users) - 2: separator = ' &' else: separator = ',' contributors.append({ 'user_id': user._primary_key, 'separator': separator, }) return { 'contributors': contributors, 'others_count': others_count, }
def project_remove_contributor(auth, **kwargs): """Remove a contributor from a list of nodes. :param Auth auth: Consolidated authorization :raises: HTTPError(400) if contributors to be removed are not in list or if no admin users would remain after changes were applied """ contributor_id = request.get_json()["contributorID"] node_ids = request.get_json()["nodeIDs"] contributor = User.load(contributor_id) if contributor is None: raise HTTPError(http.BAD_REQUEST, data={"message_long": "Contributor not found."}) redirect_url = {} parent_id = node_ids[0] for node_id in node_ids: # Update permissions and order node = Node.load(node_id) # Forbidden unless user is removing herself if not node.has_permission(auth.user, "admin"): if auth.user != contributor: raise HTTPError(http.FORBIDDEN) if len(node.visible_contributor_ids) == 1 and node.visible_contributor_ids[0] == contributor._id: raise HTTPError(http.FORBIDDEN, data={"message_long": "Must have at least one bibliographic contributor"}) nodes_removed = node.remove_contributor(contributor, auth=auth) # remove_contributor returns false if there is not one admin or visible contributor left after the move. if not nodes_removed: raise HTTPError(http.BAD_REQUEST, data={"message_long": "Could not remove contributor."}) # On parent node, if user has removed herself from project, alert; redirect to # node summary if node is public, else to user's dashboard page if not node.is_contributor(auth.user) and node_id == parent_id: status.push_status_message( "You have removed yourself as a contributor from this project", kind="success", trust=False ) if node.is_public: redirect_url = {"redirectUrl": node.url} else: redirect_url = {"redirectUrl": web_url_for("dashboard")} return redirect_url
def project_before_remove_contributor(auth, node, **kwargs): contributor = User.load(request.json.get('id')) # Forbidden unless user is removing herself if not node.has_permission(auth.user, 'admin'): if auth.user != contributor: raise HTTPError(http.FORBIDDEN) prompts = node.callback( 'before_remove_contributor', removed=contributor, ) if auth.user == contributor: prompts.insert( 0, 'Are you sure you want to remove yourself from this project?' ) return {'prompts': prompts}
def get_node_contributors_abbrev(auth, node, **kwargs): anonymous = has_anonymous_link(node, auth) formatter = 'surname' max_count = kwargs.get('max_count', 3) if 'user_ids' in kwargs: users = [ User.load(user_id) for user_id in kwargs['user_ids'] if user_id in node.visible_contributor_ids ] else: users = node.visible_contributors if anonymous or not node.can_view(auth): raise HTTPError(http.FORBIDDEN) contributors = [] n_contributors = len(users) others_count = '' for index, user in enumerate(users[:max_count]): if index == max_count - 1 and len(users) > max_count: separator = ' &' others_count = str(n_contributors - 3) elif index == len(users) - 1: separator = '' elif index == len(users) - 2: separator = ' &' else: separator = ',' contributor = user.get_summary(formatter) contributor['user_id'] = user._primary_key contributor['separator'] = separator contributors.append(contributor) return { 'contributors': contributors, 'others_count': others_count, }
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600): unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key) # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(400, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer.username unreg_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unreg_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGISTERED, user=unreg_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unreg_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def claim_user_form(auth, **kwargs): """ View for rendering the set password page for a claimed user. Must have ``token`` as a querystring argument. Renders the set password form, validates it, and sets the user's password. HTTP Method: GET, POST """ uid, pid = kwargs["uid"], kwargs["pid"] token = request.form.get("token") or request.args.get("token") user = User.load(uid) # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error if not user or not verify_claim_token(user, token, pid): error_data = { "message_short": "Invalid url.", "message_long": "Claim user does not exists, the token in the URL is invalid or has expired.", } raise HTTPError(http.BAD_REQUEST, data=error_data) # If user is logged in, redirect to 're-enter password' page if auth.logged_in: return redirect(web_url_for("claim_user_registered", uid=uid, pid=pid, token=token)) unclaimed_record = user.unclaimed_records[pid] user.fullname = unclaimed_record["name"] user.update_guessed_names() # The email can be the original referrer email if no claimer email has been specified. claimer_email = unclaimed_record.get("claimer_email") or unclaimed_record.get("email") # If there is a registered user with this email, redirect to 're-enter password' page found_by_email = User.find_by_email(claimer_email) user_from_email = found_by_email[0] if found_by_email else None if user_from_email and user_from_email.is_registered: return redirect(web_url_for("claim_user_registered", uid=uid, pid=pid, token=token)) form = SetEmailAndPasswordForm(request.form, token=token) if request.method == "POST": if not form.validate(): forms.push_errors_to_status(form.errors) elif settings.RECAPTCHA_SITE_KEY and not validate_recaptcha( request.form.get("g-recaptcha-response"), remote_ip=request.remote_addr ): status.push_status_message("Invalid captcha supplied.", kind="error") else: username, password = claimer_email, form.password.data if not username: raise HTTPError( http.BAD_REQUEST, data=dict( message_long="No email associated with this account. Please claim this " "account on the project to which you were invited." ), ) user.register(username=username, password=password) # Clear unclaimed records user.unclaimed_records = {} user.verification_key = generate_verification_key() user.save() # Authenticate user and redirect to project page status.push_status_message(language.CLAIMED_CONTRIBUTOR, kind="success", trust=True) # Redirect to CAS and authenticate the user with a verification key. return redirect( cas.get_login_url( web_url_for("view_project", pid=pid, _absolute=True), username=user.username, verification_key=user.verification_key, ) ) return { "firstname": user.given_name, "email": claimer_email if claimer_email else "", "fullname": user.fullname, "form": forms.utils.jsonify(form) if is_json_request() else form, }
def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 3600, email_template='default'): """ Unregistered user claiming a user account as an contributor to a project. Send an email for claiming the account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User unclaimed_user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. :param str email_template: the email template to use :return :raise http.BAD_REQUEST """ claimer_email = email.lower().strip() unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # When adding the contributor, the referrer provides both name and email. # The given email is the same provided by user, just send to that email. if unclaimed_record.get('email') == claimer_email: mail_tpl = getattr(mails, 'INVITE_{}'.format(email_template.upper())) to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() # When adding the contributor, the referred only provides the name. # The account is later claimed by some one who provides the email. # Send email to the referrer and ask her/him to forward the email to the user. else: # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError( http.BAD_REQUEST, data=dict( message_long= 'User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # send an email to the invited user without `claim_url` if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail(claimer_email, pending_mail, user=unclaimed_user, referrer=referrer, fullname=unclaimed_record['name'], node=node) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username # send an email to the referrer with `claim_url` mails.send_mail(to_addr, mail_tpl, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name']) return to_addr
def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 3600, email_template='default'): """ Unregistered user claiming a user account as an contributor to a project. Send an email for claiming the account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User unclaimed_user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. :param str email_template: the email template to use :return :raise http.BAD_REQUEST """ claimer_email = email.lower().strip() unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # Option 1: # When adding the contributor, the referrer provides both name and email. # The given email is the same provided by user, just send to that email. preprint_provider = None if unclaimed_record.get('email') == claimer_email: # check email template for branded preprints if email_template == 'preprint': email_template, preprint_provider = find_preprint_provider(node) if not email_template or not preprint_provider: return mail_tpl = getattr(mails, 'INVITE_PREPRINT')(email_template, preprint_provider) else: mail_tpl = getattr(mails, 'INVITE_DEFAULT'.format(email_template.upper())) to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() # Option 2: # TODO: [new improvement ticket] this option is disabled from preprint but still available on the project page # When adding the contributor, the referred only provides the name. # The account is later claimed by some one who provides the email. # Send email to the referrer and ask her/him to forward the email to the user. else: # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(http.BAD_REQUEST, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # send an email to the invited user without `claim_url` if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail( claimer_email, pending_mail, user=unclaimed_user, referrer=referrer, fullname=unclaimed_record['name'], node=node ) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username # Send an email to the claimer (Option 1) or to the referrer (Option 2) with `claim_url` mails.send_mail( to_addr, mail_tpl, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name'], branded_service_name=preprint_provider ) return to_addr
def claim_user_form(auth, **kwargs): """ View for rendering the set password page for a claimed user. Must have ``token`` as a querystring argument. Renders the set password form, validates it, and sets the user's password. HTTP Method: GET, POST """ uid, pid = kwargs['uid'], kwargs['pid'] token = request.form.get('token') or request.args.get('token') user = User.load(uid) # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error if not user or not verify_claim_token(user, token, pid): error_data = { 'message_short': 'Invalid url.', 'message_long': 'Claim user does not exists, the token in the URL is invalid or has expired.' } raise HTTPError(http.BAD_REQUEST, data=error_data) # If user is logged in, redirect to 're-enter password' page if auth.logged_in: return redirect( web_url_for('claim_user_registered', uid=uid, pid=pid, token=token)) unclaimed_record = user.unclaimed_records[pid] user.fullname = unclaimed_record['name'] user.update_guessed_names() # The email can be the original referrer email if no claimer email has been specified. claimer_email = unclaimed_record.get( 'claimer_email') or unclaimed_record.get('email') form = SetEmailAndPasswordForm(request.form, token=token) if request.method == 'POST': if not form.validate(): forms.push_errors_to_status(form.errors) elif settings.RECAPTCHA_SITE_KEY and not validate_recaptcha( request.form.get('g-recaptcha-response'), remote_ip=request.remote_addr): status.push_status_message('Invalid captcha supplied.', kind='error') else: username, password = claimer_email, form.password.data if not username: raise HTTPError( http.BAD_REQUEST, data=dict( message_long= 'No email associated with this account. Please claim this ' 'account on the project to which you were invited.')) user.register(username=username, password=password) # Clear unclaimed records user.unclaimed_records = {} user.verification_key = generate_verification_key() user.save() # Authenticate user and redirect to project page status.push_status_message(language.CLAIMED_CONTRIBUTOR, kind='success', trust=True) # Redirect to CAS and authenticate the user with a verification key. return redirect( cas.get_login_url(web_url_for('view_project', pid=pid, _absolute=True), username=user.username, verification_key=user.verification_key)) return { 'firstname': user.given_name, 'email': claimer_email if claimer_email else '', 'fullname': user.fullname, 'form': forms.utils.jsonify(form) if is_json_request() else form, }
def test_get_user_by_id(self): user = UserFactory() assert_equal(User.load(user._id), user)
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600, email_template='default'): """Send an email for claiming a user account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. """ claimer_email = email.lower().strip() unclaimed_record = user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = user.get_claim_url(node._primary_key, external=True) # If given email is the same provided by user, just send to that email if unclaimed_record.get('email') == claimer_email: mail_tpl = getattr(mails, 'INVITE_{}'.format(email_template.upper())) to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email user.save() else: # Otherwise have the referrer forward the email to the user # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError( 400, data=dict( message_long= 'User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer_email user.save() claim_url = user.get_claim_url(node._primary_key, external=True) if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail(claimer_email, pending_mail, user=user, referrer=referrer, fullname=unclaimed_record['name'], node=node) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username mails.send_mail(to_addr, mail_tpl, user=user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name']) return to_addr
def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 3600): """ A registered user claiming the unclaimed user account as an contributor to a project. Send an email for claiming the account to the referrer and notify the claimer. :param claimer: the claimer :param unclaimed_user: the user account to claim :param node: the project node where the user account is claimed :param throttle: the time period in seconds before another claim for the account can be made :return: :raise: http.BAD_REQUEST """ unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError( http.BAD_REQUEST, data=dict( message_long= 'User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer.username unclaimed_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unclaimed_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGISTERED, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unclaimed_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def deserialize_contributors(node, user_dicts, auth, validate=False): """View helper that returns a list of User objects from a list of serialized users (dicts). The users in the list may be registered or unregistered users. e.g. ``[{'id': 'abc123', 'registered': True, 'fullname': ..}, {'id': None, 'registered': False, 'fullname'...}, {'id': '123ab', 'registered': False, 'fullname': ...}] If a dict represents an unregistered user without an ID, creates a new unregistered User record. :param Node node: The node to add contributors to :param list(dict) user_dicts: List of serialized users in the format above. :param Auth auth: :param bool validate: Whether to validate and sanitize fields (if necessary) """ # Add the registered contributors contribs = [] for contrib_dict in user_dicts: fullname = contrib_dict['fullname'] visible = contrib_dict['visible'] email = contrib_dict.get('email') if validate is True: # Validate and sanitize inputs as needed. Email will raise error if invalid. # TODO Edge case bug: validation and saving are performed in same loop, so all in list # up to the invalid entry will be saved. (communicate to the user what needs to be retried) fullname = sanitize.strip_html(fullname) if not fullname: raise ValidationValueError('Full name field cannot be empty') if email: validate_email(email) # Will raise a ValidationError if email invalid if contrib_dict['id']: contributor = User.load(contrib_dict['id']) else: try: contributor = User.create_unregistered( fullname=fullname, email=email) contributor.save() except ValidationValueError: ## FIXME: This suppresses an exception if ID not found & new validation fails; get_user will return None contributor = get_user(email=email) # Add unclaimed record if necessary if (not contributor.is_registered and node._primary_key not in contributor.unclaimed_records): contributor.add_unclaimed_record(node=node, referrer=auth.user, given_name=fullname, email=email) contributor.save() contribs.append({ 'user': contributor, 'visible': visible, 'permissions': expand_permissions(contrib_dict.get('permission')) }) return contribs
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 = web_url_for('auth_login', logout=True, next=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): logout_url = web_url_for('auth_logout', redirect_url=request.url) 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(logout_url) } raise HTTPError(http.BAD_REQUEST, data=data) uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token'] unreg_user = User.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} form = PasswordForm(request.form) if request.method == 'POST': if form.validate(): if current_user.check_password(form.password.data): 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) else: status.push_status_message(language.LOGIN_FAILED, kind='warning', trust=False) else: forms.push_errors_to_status(form.errors) 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}