def participant1_username(request, test_session, participant1_user): """A username for participant1_user""" from assembl.models import Username username = Username(user=participant1_user, username="******") test_session.add(username) test_session.flush() def fin(): print "finalizer participant1_username" test_session.delete(username) test_session.flush() request.addfinalizer(fin) return username
def add_user(name, email, password, role, force=False, username=None, localrole=None, discussion=None, change_old_password=True, **kwargs): from assembl.models import Discussion, Username db = Discussion.default_db # refetch within transaction all_roles = {r.name: r for r in Role.default_db.query(Role).all()} user = None created_user = True if discussion and localrole: if isinstance(discussion, (str, unicode)): discussion_ob = db.query(Discussion).filter_by( slug=discussion).first() assert discussion_ob,\ "Discussion with slug %s does not exist" % (discussion,) elif isinstance(discussion, int): discussion_ob = db.query(Discussion).get(discussion) discussion = discussion_ob assert discussion existing_email = db.query(EmailAccount).filter( EmailAccount.email_ci == email).first() assert force or not existing_email,\ "User with email %s already exists" % (email,) if username: existing_username = db.query(Username).filter_by( username=username).first() assert force or not existing_username,\ "User with username %s already exists" % (username,) assert not existing_email or not existing_username or \ existing_username.user == existing_email.profile,\ "Two different users already exist with "\ "username %s and email %s." % (username, email) if existing_email: user = existing_email.profile elif username and existing_username: user = existing_username.user old_user = isinstance(user, User) if old_user: user.preferred_email = email user.name = name user.verified = True created_user = False if change_old_password: if password is None: user.password = None else: user.password_p = password if username: if user.username: user.username.username = username else: db.add(Username(username=username, user=user)) else: if user: # Profile may have come from userless existing AgentProfile user = user.change_class(User, None, preferred_email=email, verified=True, creation_date=datetime.utcnow()) if password is not None: user.password_p = password else: user = User(name=name, preferred_email=email, verified=True, password=password, creation_date=datetime.utcnow()) db.add(user) if username: db.add(Username(username=username, user=user)) for account in user.accounts: if isinstance(account, EmailAccount) and account.email_ci == email: account.verified = True account.preferred = True break else: account = EmailAccount(profile=user, email=email, preferred=True, verified=True) db.add(account) if role: role = all_roles[role] ur = None if old_user: ur = db.query(UserRole).filter_by(user=user, role=role).first() if not ur: db.add(UserRole(user=user, role=role)) created_localrole = False if localrole: localrole = all_roles[localrole] lur = None if old_user: lur = db.query(LocalUserRole).filter_by(user=user, discussion=discussion, role=localrole).first() if not lur: created_localrole = True db.add( LocalUserRole(user=user, role=localrole, discussion=discussion)) # Do this at login # if discussion: # user.get_notification_subscriptions(discussion.id) db.flush() return (user, created_user, created_localrole)
def assembl_profile(request): session = AgentProfile.default_db localizer = request.localizer profile = get_profile(request) id_type = request.matchdict.get('type').strip() logged_in = authenticated_userid(request) save = request.method == 'POST' # if some other user if not profile or not logged_in or logged_in != profile.id: if save: raise HTTPUnauthorized() # Add permissions to view a profile? return render_to_response( 'assembl:templates/view_profile.jinja2', dict(get_default_context(request), profile=profile, user=logged_in and session.query(User).get(logged_in))) confirm_email = request.params.get('confirm_email', None) if confirm_email: return HTTPTemporaryRedirect(location=request.route_url( 'confirm_emailid_sent', email_account_id=int(confirm_email))) errors = [] if save: user_id = profile.id redirect = False username = request.params.get('username', '').strip() if username and ( profile.username is None or username != profile.username.username): # check if exists if session.query(Username).filter_by(username=username).count(): errors.append(localizer.translate(_( 'The username %s is already used')) % (username,)) else: old_username = profile.username if old_username is not None: # free existing username session.delete(old_username) session.flush() # add new username session.add(Username(username=username, user=profile)) if id_type == 'u': redirect = True name = request.params.get('name', '').strip() if name: profile.name = name p1, p2 = (request.params.get('password1', '').strip(), request.params.get('password2', '').strip()) if p1 != p2: errors.append(localizer.translate(_( 'The passwords are not identical'))) elif p1: profile.password_p = p1 add_email = request.params.get('add_email', '').strip() if add_email: if not is_email(add_email): return dict(get_default_context(request), error=localizer.translate(_( "This is not a valid email"))) # No need to check presence since not validated yet email = EmailAccount( email=add_email, profile=profile) session.add(email) if redirect: return HTTPFound(location=request.route_url( 'profile_user', type='u', identifier=username)) profile = session.query(User).get(user_id) unverified_emails = [ (ea, session.query(AbstractAgentAccount).filter_by( email_ci=ea.email_ci, verified=True).first()) for ea in profile.email_accounts if not ea.verified] get_route = create_get_route(request) providers = get_provider_data(get_route) return render_to_response( 'assembl:templates/profile.jinja2', dict(get_default_context(request), error='<br />'.join(errors), unverified_emails=unverified_emails, providers=providers, providers_json=json.dumps(providers), google_consumer_key=request.registry.settings.get( 'google.consumer_key', ''), the_user=profile, user=session.query(User).get(logged_in)))
def velruse_login_complete_view(request): # TODO: Write tests. Situations are a combinatorics of the folloing: # 1. User action: # A. Social login # B. Logged in, add social account # C. Logged in, add email account (does not happen here) # 2. Is there an existing account with that email? # A. No. # B. The social account already exists # C. A valid email account # D. An invalid email account, sole account of profile # E. An invalid email account, but profile has other accounts # F. A social account from a different provider # 3. When we're logged in (1B, 1C), is the existing account # on the logged in profile or another? # 4. If gmail account, is it an enterprise account? session = AgentProfile.default_db context = request.context now = datetime.utcnow() velruse_profile = context.profile discussion = None slug = request.session.get('discussion', None) if not slug: discussion = discussion_from_request(request) if discussion: slug = discussion.slug if slug and not discussion: discussion = session.query(Discussion).filter_by(slug=slug).first() next_view = handle_next_view(request, True) logged_in = authenticated_userid(request) if logged_in: logged_in = User.get(logged_in) base_profile = logged_in provider = get_identity_provider(request) # find or create IDP_Accounts idp_accounts = [] new_idp_account = None velruse_accounts = velruse_profile['accounts'] old_autoflush = session.autoflush # sqla mislikes creating accounts before profiles, so delay session.autoflush = False # Search for this social account for velruse_account in velruse_accounts: if 'userid' in velruse_account: idp_accounts.extend( session.query(IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], userid=velruse_account['userid']).all()) elif 'username' in velruse_account: idp_accounts.extend( session.query(IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], username=velruse_account['username']).all()) else: log.error("account needs username or email" + velruse_account) raise HTTPServerError("account needs username or userid") trusted_emails = set() if idp_accounts: for idp_account in idp_accounts: idp_account.profile_info_json = velruse_profile if len(idp_accounts) > 1: log.warn("multiple idp_accounts:" + ','.join((a.id for a in idp_accounts)) + " for " + velruse_accounts) # We will just the last one from the loop for now. trusted_emails.update([ a.email for a in idp_accounts if a.provider.trust_emails and a.email ]) else: # Create it if not found idp_class = IdentityProviderAccount for cls in idp_class.get_subclasses(): if cls == idp_class: continue if cls.account_provider_name == provider.name: idp_class = cls break idp_account = idp_class(provider=provider, profile_info_json=velruse_profile, domain=velruse_account.get('domain'), userid=velruse_account.get('userid'), verified=True, username=velruse_account.get('username')) idp_accounts.append(idp_account) new_idp_account = base_account = idp_account session.add(idp_account) for account in idp_accounts: if account.provider.trust_emails: profile = (velruse_profile if account == new_idp_account else account.profile_info_json) email = profile.get('verifiedEmail', None) if email: if account == new_idp_account: account.email = email trusted_emails.add(email) for email in profile.get('emails', ()): if isinstance(email, dict): email = email.get('value', None) if isinstance(email, (str, unicode)) and email: trusted_emails.add(email) if account == new_idp_account and not account.email: account.email = email # else we have created an email-less account. Treat accordingly. conflicting_profiles = {a.profile for a in idp_accounts if a.profile} # Are there other accounts/profiles than the ones we know? conflicting_accounts = set() for email in trusted_emails: other_accounts = session.query(AbstractAgentAccount).filter_by( email=email) for account in other_accounts: conflicting_accounts.add(account) if account.verified or len(account.profile.accounts) == 1: conflicting_profiles.add(account.profile) # choose best known profile for base_account # prefer profiles with verified users, then users, then oldest profiles profile_list = list(conflicting_profiles) profile_list.sort(key=lambda p: (not (isinstance(p, User) and p.verified), not isinstance(p, User), p.id)) if new_idp_account and profile_list: base_profile = profile_list[0] elif not new_idp_account: # Take first appropriate. Should be the first, somehow not. for profile in profile_list: accounts = [a for a in idp_accounts if a.profile == profile] if accounts: base_account = accounts[0] break if not logged_in: base_profile = base_account.profile new_profile = None if new_idp_account: if not base_profile: # Create a new user base_profile = new_profile = User(name=velruse_profile.get( 'displayName', ''), verified=True, creation_date=now) session.add(new_profile) # Upgrade a AgentProfile if not isinstance(base_profile, User): base_profile = base_profile.change_class(User, None, verified=True) new_idp_account.profile = base_profile base_profile.last_login = now base_profile.verified = True if not base_profile.name: base_profile.name = velruse_profile.get('displayName', None) # TODO (needs parsing) # base_profile.timezone=velruse_profile['utcOffset'] # Now all accounts have a profile session.autoflush = old_autoflush session.flush() # Merge other profiles with the same (validated) email # TODO: Ask the user about doing this. conflicting_profiles.discard(base_profile) conflicting_accounts.discard(base_account) for conflicting_profile in conflicting_profiles: base_profile.merge(conflicting_profile) session.delete(conflicting_profile) # If base_profile is still an AgentProfile at this point # then it needs to be upgraded to a User if not isinstance(base_profile, User): base_profile = base_profile.change_class(User, None, verified=True, last_login=now) # Set username if not base_profile.username: username = None usernames = set((a['preferredUsername'] for a in velruse_accounts if 'preferredUsername' in a)) for u in usernames: if not session.query(Username).filter_by(username=u).count(): username = u break if username: session.add(Username(username=username, user=base_profile)) # Create AgentStatusInDiscussion if new_profile and discussion: agent_status = AgentStatusInDiscussion( agent_profile=base_profile, discussion=discussion, user_created_on_this_discussion=True) session.add(agent_status) if maybe_auto_subscribe(base_profile, discussion): next_view = "/%s/" % (slug, ) # Delete other (email) accounts if base_account.provider.trust_emails: for account in conflicting_accounts: # Merge may have been confusing session.expire(account) account = AbstractAgentAccount.get(account.id) if account.profile == base_profile: if account.email == base_account.email: if isinstance(account, EmailAccount): account.delete() if account.verified and account.preferred: base_account.preferred = True elif isinstance(account, IdentityProviderAccount): if account.provider_id == base_account.provider_id: log.error("This should have been caught earlier") account.delete() else: log.warning("Two accounts with same email," + "different provider: %d, %d" % (account.id, base_account.id)) else: # If they're verified, they should have been merged. if account.verified: log.error("account %d should not exist: " % (account.id, )) else: other_profile = account.profile account.delete() session.flush() session.expire(other_profile, ["accounts"]) if not len(other_profile.accounts): log.warning("deleting profile %d" % other_profile.id) other_profile.delete() session.expire(base_profile, ['accounts', 'email_accounts']) # create an email account for other emails. known_emails = {a.email for a in base_profile.accounts} for email in trusted_emails: if email not in known_emails: email = EmailAccount(email=email, profile=base_profile, verified=base_account.provider.trust_emails) session.add(email) session.flush() base_profile.last_login = now headers = remember(request, base_profile.id) request.response.headerlist.extend(headers) if discussion: request.session['discussion'] = discussion.slug return HTTPFound(location=next_view)
def add_user(name, email, password=None, role=R_SYSADMIN, force=False, username=None, localrole=None, discussion=None, change_old_password=True, send_password_change=False, resend_if_not_logged_in=False, text_message=None, html_message=None, sender_name=None, message_subject=None, db=None, request=None, flush=True, **kwargs): from assembl.models import Discussion, Username db = db or Discussion.default_db # refetch within transaction all_roles = {r.name: r for r in db.query(Role).all()} user = None created_user = True if discussion and not isinstance(discussion, Discussion): if isinstance(discussion, (str, unicode)): discussion = db.query(Discussion).filter_by( slug=discussion).first() assert discussion,\ "Discussion with slug %s does not exist" % (discussion,) elif isinstance(discussion, int): discussion = db.query(Discussion).get(discussion) assert discussion existing_email = None if email: existing_email = db.query(EmailAccount).filter( EmailAccount.email_ci == email).first() assert force or not existing_email,\ "User with email %s already exists" % (email,) if username: existing_username = db.query(Username).filter_by( username=username).first() assert force or not existing_username,\ "User with username %s already exists" % (username,) assert not existing_email or not existing_username or \ existing_username.user == existing_email.profile,\ "Two different users already exist with "\ "username %s and email %s." % (username, email) if existing_email: user = existing_email.profile elif username and existing_username: user = existing_username.user old_user = isinstance(user, User) if old_user: user.preferred_email = email user.name = name user.verified = True created_user = False if change_old_password: if password is None: user.password = None else: user.password_p = password if username: if user.username: user.username.username = username else: db.add(Username(username=username, user=user)) else: if user: # Profile may have come from userless existing AgentProfile user = user.change_class(User, None, preferred_email=email, verified=True, creation_date=datetime.utcnow(), is_machine=kwargs.get( 'is_machine', False)) if password is not None: user.password_p = password else: user = User(name=name, preferred_email=email, verified=True, password=password, creation_date=datetime.utcnow(), is_machine=kwargs.get('is_machine', False)) db.add(user) if username: db.add(Username(username=username, user=user)) for account in user.accounts: if isinstance(account, EmailAccount) and account.email_ci == email: account.verified = True account.preferred = True break else: account = EmailAccount(profile=user, email=email, preferred=True, verified=True) db.add(account) if role: role = all_roles[role] ur = None if old_user: ur = db.query(UserRole).filter_by(user=user, role=role).first() if not ur: db.add(UserRole(user=user, role=role)) created_localrole = False if localrole and discussion: localrole = all_roles[localrole] lur = None if old_user: lur = db.query(LocalUserRole).filter_by(user=user, discussion=discussion, role=localrole).first() if not lur: created_localrole = True db.add( LocalUserRole(user=user, role=localrole, discussion=discussion)) if flush: db.flush() status_in_discussion = None if resend_if_not_logged_in and discussion and not (created_user or created_localrole): status_in_discussion = user.get_status_in_discussion(discussion.id) if send_password_change and (created_user or created_localrole or (resend_if_not_logged_in and (status_in_discussion is None or not status_in_discussion.first_visit))): from assembl.views.auth.views import send_change_password_email closer = None request = request or get_current_request() if not request: request, closer = _make_fake_request(discussion) send_change_password_email(request, user, email, subject=message_subject, text_body=text_message, html_body=html_message, discussion=discussion, sender_name=sender_name, welcome=True) if closer: closer() return (user, created_user, created_localrole)