def test_new_user_created_and_invitation_accepted(self): """ When SsoBackend creates a new user and an invitation is present, that invitation should add the user to the invited project space and accept the invitation """ admin_role = UserRole.admin_role(self.domain.name) invitation = Invitation( domain=self.domain.name, email='*****@*****.**', invited_by=self.user.couch_id, invited_on=datetime.datetime.utcnow(), role=admin_role.get_qualified_id(), ) invitation.save() prepare_session_for_sso_invitation(self.request, invitation) generator.store_full_name_in_saml_user_data(self.request, 'Isa', 'Baas') user = auth.authenticate( request=self.request, username=invitation.email, idp_slug=self.idp.slug, is_handshake_successful=True, ) self.assertIsNotNone(user) self.assertEqual(user.username, invitation.email) self.assertEqual(user.first_name, 'Isa') self.assertEqual(user.last_name, 'Baas') self.assertEqual(self.request.sso_new_user_messages['success'], [ f'User account for {invitation.email} created.', f'You have been added to the "{invitation.domain}" project space.', ])
def invite_web_user(request, domain, template="users/invite_web_user.html"): role_choices = UserRole.role_choices(domain) if request.method == "POST": form = AdminInvitesUserForm(request.POST, excluded_emails=[user.username for user in WebUser.by_domain(domain)], role_choices=role_choices ) if form.is_valid(): data = form.cleaned_data # create invitation record data["invited_by"] = request.couch_user.user_id data["invited_on"] = datetime.utcnow() data["domain"] = domain invite = Invitation(**data) invite.save() invite.send_activation_email() messages.success(request, "Invitation sent to %s" % invite.email) return HttpResponseRedirect(reverse("web_users", args=[domain])) else: form = AdminInvitesUserForm(role_choices=role_choices) context = _users_context(request, domain) context.update( registration_form=form ) return render_to_response(request, template, context)
def setUpClass(cls): cls.invitations = [] for kwargs in [ {'domain': 'domain_1', 'email': '*****@*****.**'}, {'domain': 'domain_1', 'email': '*****@*****.**', 'is_accepted': True}, {'domain': 'domain_2', 'email': '*****@*****.**'}, ]: inv = Invitation(**kwargs) inv.save() cls.invitations.append(inv)
def setUpClass(cls): super(InvitationTest, cls).setUpClass() cls.invitations = [] for kwargs in [ {'domain': 'domain_1', 'email': '*****@*****.**'}, {'domain': 'domain_1', 'email': '*****@*****.**', 'is_accepted': True}, {'domain': 'domain_2', 'email': '*****@*****.**'}, ]: inv = Invitation(**kwargs) inv.save() cls.invitations.append(inv)
def rows_for_domain(self, domain_obj): rows = [] for user in get_all_user_rows(domain_obj.name, include_web_users=True, include_mobile_users=False, include_inactive=False, include_docs=True): user = CouchUser.wrap_correctly(user['doc']) domain_membership = user.get_domain_membership(domain_obj.name) last_accessed_domain = None if domain_membership: last_accessed_domain = domain_membership.last_accessed rows.append([ user.username, user.full_name, user.role_label(domain_obj.name), self.format_date(user.last_login), last_accessed_domain, _('Active User') ] + self.domain_properties(domain_obj)) for invite in Invitation.by_domain(domain_obj.name): rows.append([ invite.email, 'N/A', invite.get_role_name(), 'N/A', 'N/A', _('Invited') ] + self.domain_properties(domain_obj)) return rows
def invite_web_user_form(self): role_choices = _get_editable_role_choices(self.domain, self.request.couch_user, allow_admin_role=True) loc = None domain_request = DomainRequest.objects.get( id=self.request_id) if self.request_id else None initial = { 'email': domain_request.email if domain_request else None, } if 'location_id' in self.request.GET: from corehq.apps.locations.models import SQLLocation loc = SQLLocation.objects.get( location_id=self.request.GET.get('location_id')) if self.request.method == 'POST': current_users = [ user.username for user in WebUser.by_domain(self.domain) ] pending_invites = [ di.email for di in Invitation.by_domain(self.domain) ] return AdminInvitesUserForm(self.request.POST, excluded_emails=current_users + pending_invites, role_choices=role_choices, domain=self.domain) return AdminInvitesUserForm(initial=initial, role_choices=role_choices, domain=self.domain, location=loc)
def parse_web_users(domain, task=None, total_count=None): user_dicts = [] max_location_length = 0 location_cache = LocationIdToSiteCodeCache(domain) for n, user in enumerate(get_all_user_rows(domain, include_web_users=True, include_mobile_users=False, include_inactive=False, include_docs=True)): user_dict = make_web_user_dict(user, location_cache, domain) user_dicts.append(user_dict) max_location_length = max(max_location_length, len(user_dict["location_code"])) if task: DownloadBase.set_progress(task, n, total_count) for m, invite in enumerate(Invitation.by_domain(domain)): user_dict = make_invited_web_user_dict(invite, location_cache) user_dicts.append(user_dict) if task: DownloadBase.set_progress(task, n + m, total_count) user_headers = [ 'username', 'first_name', 'last_name', 'email', 'role', 'last_access_date (read only)', 'last_login (read only)', 'status', 'remove' ] if domain_has_privilege(domain, privileges.LOCATIONS): user_headers.extend(json_to_headers( {'location_code': list(range(1, max_location_length + 1))} )) return user_headers, get_user_rows(user_dicts, user_headers)
def redirect_to_on_success(self, email, domain): if Invitation.by_email(email).count() > 0 and not self.request.GET.get( 'no_redirect'): return reverse("domain_select_redirect") else: return reverse("domain_homepage", args=[ domain, ])
def reinvite_web_user(request, domain): invitation_id = request.POST["invite"] try: invitation = Invitation.get(invitation_id) invitation.invited_on = datetime.utcnow() invitation.save() invitation.send_activation_email() return json_response({"response": _("Invitation resent"), "status": "ok"}) except ResourceNotFound: return json_response({"response": _("Error while attempting resend"), "status": "error"})
def reinvite_web_user(request, domain): invitation_id = request.POST['invite'] try: invitation = Invitation.get(invitation_id) invitation.invited_on = datetime.utcnow() invitation.save() invitation.send_activation_email() return json_response({'response': _("Invitation resent"), 'status': 'ok'}) except ResourceNotFound: return json_response({'response': _("Error while attempting resend"), 'status': 'error'})
def setUpClass(cls): super(InvitationTest, cls).setUpClass() cls.invitations = [ Invitation(domain='domain_1', email='*****@*****.**', invited_by='*****@*****.**', invited_on=datetime.utcnow()), Invitation(domain='domain_1', email='*****@*****.**', invited_by='*****@*****.**', invited_on=datetime.utcnow(), is_accepted=True), Invitation(domain='domain_2', email='*****@*****.**', invited_by='*****@*****.**', invited_on=datetime.utcnow()), ] for inv in cls.invitations: inv.save()
def resend_pending_invitations(): from corehq.apps.users.models import Invitation days_to_resend = (15, 29) days_to_expire = 30 domains = Domain.get_all() for domain in domains: invitations = Invitation.by_domain(domain.name) for invitation in invitations: days = (datetime.utcnow() - invitation.invited_on).days if days in days_to_resend: invitation.send_activation_email(days_to_expire - days)
def invitations(self): return [ { "uuid": str(invitation.uuid), "email": invitation.email, "email_marked_as_bounced": bool(invitation.email_marked_as_bounced), "invited_on": invitation.invited_on, "role_label": self.role_labels.get(invitation.role, ""), } for invitation in Invitation.by_domain(self.domain) ]
def accept_all_invitations(request): user = request.couch_user invites = Invitation.by_email(user.username) for invitation in invites: if not invitation.is_expired: invitation.accept_invitation_and_join_domain(user) messages.success( request, _(f'You have been added to the "{invitation.domain}" project space.' )) return HttpResponseRedirect(reverse('domain_select_redirect'))
def test_update_pending_user_role(self, mock_send_activation_email): import_users_and_groups( self.domain.name, [ self._get_spec( web_user='******', is_account_confirmed='False', send_confirmation_email='True', role=self.role.name ) ], [], self.uploading_user, mock.MagicMock() ) self.assertEqual(mock_send_activation_email.call_count, 1) self.assertEqual(self.user.get_role(self.domain_name).name, self.role.name) self.assertEqual(Invitation.by_email('*****@*****.**')[0].role.split(":")[1], self.role._id) added_user_id = self.user._id import_users_and_groups( self.domain.name, [ self._get_spec( web_user='******', user_id=added_user_id, is_account_confirmed='False', send_confirmation_email='True', role=self.other_role.name ) ], [], self.uploading_user, mock.MagicMock() ) self.assertEqual(mock_send_activation_email.call_count, 1) # invite only sent once self.assertEqual(len(Invitation.by_email('*****@*****.**')), 1) # only one invite associated with user self.assertEqual(self.user.get_role(self.domain.name).name, self.other_role.name) self.assertEqual(Invitation.by_email('*****@*****.**')[0].role, self.other_role.get_qualified_id())
def select(request, do_not_redirect=False, next_view=None): if not hasattr(request, 'couch_user'): return redirect('registration_domain') # next_view must be a url that expects exactly one parameter, a domain name next_view = next_view or request.GET.get('next_view') show_invitations = False if not next_view: next_view = "domain_homepage" show_invitations = True domain_links = get_domain_links_for_dropdown(request.couch_user, view_name=next_view) if not domain_links: return redirect('registration_domain') domain_links += get_mirror_domain_links_for_dropdown(request.couch_user, view_name=next_view) domain_links = sorted(domain_links, key=lambda link: link['display_name'].lower()) email = request.couch_user.get_email() open_invitations = [e for e in Invitation.by_email(email) if not e.is_expired] additional_context = { 'domain_links': domain_links, 'invitation_links': [{ 'display_name': i.domain, 'url': reverse("domain_accept_invitation", args=[i.domain, i.uuid]) + '?no_redirect=true', } for i in open_invitations] if show_invitations else [], 'current_page': {'page_name': _('Select A Project')}, } domain_select_template = "domain/select.html" last_visited_domain = get_last_visited_domain(request.couch_user) if open_invitations \ or do_not_redirect \ or not last_visited_domain: return render(request, domain_select_template, additional_context) else: domain_obj = Domain.get_by_name(last_visited_domain) if domain_obj and domain_obj.is_active: # mirrors logic in login_and_domain_required if ( request.couch_user.is_member_of(domain_obj, allow_mirroring=True) or (request.user.is_superuser and not domain_obj.restrict_superusers) or domain_obj.is_snapshot ): try: return HttpResponseRedirect(reverse(next_view or 'dashboard_default', args=[last_visited_domain])) except Http404: pass set_last_visited_domain(request.couch_user, None) return render(request, domain_select_template, additional_context)
def post(self, request, *args, **kwargs): if self.invite_web_user_form.is_valid(): # If user exists and has already requested access, just add them to the project # Otherwise, send an invitation create_invitation = True data = self.invite_web_user_form.cleaned_data domain_request = DomainRequest.by_email(self.domain, data["email"]) if domain_request is not None: domain_request.is_approved = True domain_request.save() user = CouchUser.get_by_username(domain_request.email) if user is not None: domain_request.send_approval_email() create_invitation = False user.add_as_web_user(self.domain, role=data["role"], location_id=data.get("supply_point", None), program_id=data.get("program", None)) messages.success(request, "%s added." % data["email"]) else: track_workflow(request.couch_user.get_email(), "Sent a project invitation", {"Sent a project invitation": "yes"}) meta = get_meta(request) track_sent_invite_on_hubspot.delay(request.couch_user, request.COOKIES, meta) messages.success(request, "Invitation sent to %s" % data["email"]) if create_invitation: data["invited_by"] = request.couch_user.user_id data["invited_on"] = datetime.utcnow() data["domain"] = self.domain invite = Invitation(**data) invite.save() invite.send_activation_email() return HttpResponseRedirect(reverse( ListWebUsersView.urlname, args=[self.domain] )) return self.get(request, *args, **kwargs)
def accept_all_invitations(request): def _invite(invitation, user): user.add_as_web_user(invitation.domain, role=invitation.role, location_id=invitation.supply_point, program_id=invitation.program) invitation.is_accepted = True invitation.save() send_confirmation_email(invitation) user = request.couch_user invites = Invitation.by_email(user.username) for invitation in invites: if not invitation.is_expired: _invite(invitation, user) messages.success(request, _(f'You have been added to the "{invitation.domain}" project space.')) return HttpResponseRedirect(reverse('domain_select_redirect'))
def accept_invitation(request, domain, invitation_id): if request.GET.get('switch') == 'true': logout(request) return redirect_to_login(request.path) if request.GET.get('create') == 'true': logout(request) return HttpResponseRedirect(request.path) invitation = Invitation.get(invitation_id) assert(invitation.domain == domain) if invitation.is_accepted: messages.error(request, "Sorry that invitation has already been used up. " "If you feel this is a mistake please ask the inviter for " "another invitation.") return HttpResponseRedirect(reverse("login")) if request.user.is_authenticated(): # if you are already authenticated, just add the domain to your # list of domains if request.couch_user.username != invitation.email: messages.error(request, "The invited user %s and your user %s do not match!" % (invitation.email, request.couch_user.username)) if request.method == "POST": couch_user = CouchUser.from_django_user(request.user) couch_user.add_domain_membership(domain=domain) couch_user.set_role(domain, invitation.role) couch_user.save() invitation.is_accepted = True invitation.save() messages.success(request, "You have been added to the %s domain" % domain) return HttpResponseRedirect(reverse("domain_homepage", args=[domain,])) else: return render_to_response(request, 'users/accept_invite.html', {'domain': domain, "invited_user": invitation.email if request.couch_user.username != invitation.email else ""}) else: # if you're not authenticated we need you to fill out your information if request.method == "POST": form = NewWebUserRegistrationForm(request.POST) if form.is_valid(): user = activate_new_user(form, is_domain_admin=False, domain=invitation.domain) user.set_role(domain, invitation.role) user.save() invitation.is_accepted = True invitation.save() messages.success(request, "User account for %s created! You may now login." % form.cleaned_data["email"]) return HttpResponseRedirect(reverse("login")) else: form = NewWebUserRegistrationForm(initial={'email': invitation.email}) return render_to_response(request, "users/accept_invite.html", {"form": form})
def extra_context(self): invitations = [ e for e in Invitation.by_email(self.request.user.username) if not e.is_expired ] return { 'invitation_links': [{ 'domain': i.domain, 'url': reverse("domain_accept_invitation", args=[i.domain, i.uuid]) + '?no_redirect=true', } for i in invitations], 'show_multiple_invites': len(invitations) > 1, }
def invite_web_user_form(self): role_choices = UserRole.role_choices(self.domain) loc = None domain_request = DomainRequest.objects.get(id=self.request_id) if self.request_id else None initial = {"email": domain_request.email if domain_request else None} if "location_id" in self.request.GET: from corehq.apps.locations.models import SQLLocation loc = SQLLocation.objects.get(location_id=self.request.GET.get("location_id")) if self.request.method == "POST": current_users = [user.username for user in WebUser.by_domain(self.domain)] pending_invites = [di.email for di in Invitation.by_domain(self.domain)] return AdminInvitesUserForm( self.request.POST, excluded_emails=current_users + pending_invites, role_choices=role_choices, domain=self.domain, ) return AdminInvitesUserForm(initial=initial, role_choices=role_choices, domain=self.domain, location=loc)
def select(request, domain_select_template='domain/select.html', do_not_redirect=False): domains_for_user = Domain.active_for_user(request.user) if not domains_for_user: from corehq.apps.registration.views import track_domainless_new_user track_domainless_new_user(request) return redirect('registration_domain') email = request.couch_user.get_email() open_invitations = [ e for e in Invitation.by_email(email) if not e.is_expired ] additional_context = { 'domains_for_user': domains_for_user, 'open_invitations': open_invitations, 'current_page': { 'page_name': _('Select A Project') }, } last_visited_domain = request.session.get('last_visited_domain') if open_invitations \ or do_not_redirect \ or not last_visited_domain: return render(request, domain_select_template, additional_context) else: domain_obj = Domain.get_by_name(last_visited_domain) if domain_obj and domain_obj.is_active: # mirrors logic in login_and_domain_required if (request.couch_user.is_member_of(domain_obj) or (request.user.is_superuser and not domain_obj.restrict_superusers) or domain_obj.is_snapshot): try: from corehq.apps.dashboard.views import dashboard_default return dashboard_default(request, last_visited_domain) except Http404: pass del request.session['last_visited_domain'] return render(request, domain_select_template, additional_context)
def parse_web_users(domain, task=None, total_count=None): user_dicts = [] for n, user in enumerate(get_all_user_rows(domain, include_web_users=True, include_mobile_users=False, include_inactive=False, include_docs=True)): user_dict = make_web_user_dict(user, domain) user_dicts.append(user_dict) if task: DownloadBase.set_progress(task, n, total_count) for m, invite in enumerate(Invitation.by_domain(domain)): user_dict = make_invited_web_user_dict(invite) user_dicts.append(user_dict) if task: DownloadBase.set_progress(task, n + m, total_count) user_headers = [ 'username', 'first_name', 'last_name', 'email', 'role', 'last_access_date (read only)', 'last_login (read only)', 'status', 'remove' ] return user_headers, get_user_rows(user_dicts, user_headers)
def invite_web_user_form(self): role_choices = _get_editable_role_choices(self.domain, self.request.couch_user, allow_admin_role=True) loc = None domain_request = DomainRequest.objects.get(id=self.request_id) if self.request_id else None initial = { 'email': domain_request.email if domain_request else None, } if 'location_id' in self.request.GET: from corehq.apps.locations.models import SQLLocation loc = SQLLocation.objects.get(location_id=self.request.GET.get('location_id')) if self.request.method == 'POST': current_users = [user.username for user in WebUser.by_domain(self.domain)] pending_invites = [di.email for di in Invitation.by_domain(self.domain)] return AdminInvitesUserForm( self.request.POST, excluded_emails=current_users + pending_invites, role_choices=role_choices, domain=self.domain ) return AdminInvitesUserForm(initial=initial, role_choices=role_choices, domain=self.domain, location=loc)
def _get_invitations_by_filters(domain, user_filters, count_only=False): """ Similar to _get_users_by_filters, but applites to invitations. Applies "search_string" filter to the invitations' emails. This does not support ES search syntax, it's just a case-insensitive substring search. Ignores any other filters. """ filters = {} search_string = user_filters.get("search_string", None) if search_string: filters["email__icontains"] = search_string role_id = user_filters.get("role_id", None) if role_id: role = UserRole.objects.by_couch_id(role_id) filters["role"] = role.get_qualified_id() invitations = Invitation.by_domain(domain, **filters) if count_only: return invitations.count() return invitations
def select(request, domain_select_template='domain/select.html', do_not_redirect=False): domains_for_user = Domain.active_for_user(request.user) if not domains_for_user: from corehq.apps.registration.views import track_domainless_new_user track_domainless_new_user(request) return redirect('registration_domain') email = request.couch_user.get_email() open_invitations = [e for e in Invitation.by_email(email) if not e.is_expired] additional_context = { 'domains_for_user': domains_for_user, 'open_invitations': open_invitations, 'current_page': {'page_name': _('Select A Project')}, } last_visited_domain = request.session.get('last_visited_domain') if open_invitations \ or do_not_redirect \ or not last_visited_domain: return render(request, domain_select_template, additional_context) else: domain_obj = Domain.get_by_name(last_visited_domain) if domain_obj and domain_obj.is_active: # mirrors logic in login_and_domain_required if ( request.couch_user.is_member_of(domain_obj) or (request.user.is_superuser and not domain_obj.restrict_superusers) or domain_obj.is_snapshot ): try: from corehq.apps.dashboard.views import dashboard_default return dashboard_default(request, last_visited_domain) except Http404: pass del request.session['last_visited_domain'] return render(request, domain_select_template, additional_context)
def rows_for_domain(self, domain_obj): def _get_role_name(role): if role: if role == 'admin': return role else: role_id = role[len('user-role:'):] try: return UserRole.get(role_id).name except ResourceNotFound: return _('Unknown Role') else: return 'N/A' rows = [] for user in get_all_user_rows(domain_obj.name, include_web_users=True, include_mobile_users=False, include_inactive=False, include_docs=True): user = CouchUser.wrap_correctly(user['doc']) domain_membership = user.get_domain_membership(domain_obj.name) last_accessed_domain = None if domain_membership: last_accessed_domain = domain_membership.last_accessed rows.append([ user.username, user.full_name, user.role_label(domain_obj.name), self.format_date(user.last_login), last_accessed_domain, _('Active User') ] + self.domain_properties(domain_obj)) for invite in Invitation.by_domain(domain_obj.name): rows.append([ invite.email, 'N/A', _get_role_name(invite.role), 'N/A', 'N/A', _('Invited') ] + self.domain_properties(domain_obj)) return rows
def post(self, request, *args, **kwargs): if self.invite_web_user_form.is_valid(): # If user exists and has already requested access, just add them to the project # Otherwise, send an invitation create_invitation = True data = self.invite_web_user_form.cleaned_data domain_request = DomainRequest.by_email(self.domain, data["email"]) if domain_request is not None: domain_request.is_approved = True domain_request.save() user = CouchUser.get_by_username(domain_request.email) if user is not None: domain_request.send_approval_email() create_invitation = False user.add_as_web_user(self.domain, role=data["role"], location_id=data.get( "supply_point", None), program_id=data.get("program", None)) messages.success(request, "%s added." % data["email"]) else: track_workflow(request.couch_user.get_email(), "Sent a project invitation", {"Sent a project invitation": "yes"}) meta = get_meta(request) track_sent_invite_on_hubspot.delay(request.couch_user, request.COOKIES, meta) messages.success(request, "Invitation sent to %s" % data["email"]) if create_invitation: data["invited_by"] = request.couch_user.user_id data["invited_on"] = datetime.utcnow() data["domain"] = self.domain invite = Invitation(**data) invite.save() invite.send_activation_email() return HttpResponseRedirect( reverse(ListWebUsersView.urlname, args=[self.domain])) return self.get(request, *args, **kwargs)
def test_by_email(self): self.assertEqual(len(Invitation.by_email('*****@*****.**')), 1) self.assertEqual(len(Invitation.by_email('*****@*****.**')), 1) self.assertEqual(len(Invitation.by_email('*****@*****.**')), 0)
def __call__(self, request, invitation_id, **kwargs): logging.warning("Don't use this view in more apps until it gets cleaned up.") # add the correct parameters to this instance self.request = request self.inv_id = invitation_id if "domain" in kwargs: self.domain = kwargs["domain"] if request.GET.get("switch") == "true": logout(request) return redirect_to_login(request.path) if request.GET.get("create") == "true": logout(request) return HttpResponseRedirect(request.path) try: invitation = Invitation.get(invitation_id) except ResourceNotFound: messages.error( request, _( "Sorry, it looks like your invitation has expired. " "Please check the invitation link you received and try again, or request a " "project administrator to send you the invitation again." ), ) return HttpResponseRedirect(reverse("login")) if invitation.is_accepted: messages.error( request, _( "Sorry, that invitation has already been used up. " "If you feel this is a mistake please ask the inviter for " "another invitation." ), ) return HttpResponseRedirect(reverse("login")) self.validate_invitation(invitation) if invitation.is_expired: return HttpResponseRedirect(reverse("no_permissions")) context = self.added_context() if request.user.is_authenticated(): is_invited_user = request.couch_user.username.lower() == invitation.email.lower() if self.is_invited(invitation, request.couch_user) and not request.couch_user.is_superuser: if is_invited_user: # if this invite was actually for this user, just mark it accepted messages.info( request, _("You are already a member of {entity}.").format(entity=self.inviting_entity) ) invitation.is_accepted = True invitation.save() else: messages.error( request, _( "It looks like you are trying to accept an invitation for " "{invited} but you are already a member of {entity} with the " "account {current}. Please sign out to accept this invitation " "as another user." ).format( entity=self.inviting_entity, invited=invitation.email, current=request.couch_user.username ), ) return HttpResponseRedirect(self.redirect_to_on_success) if not is_invited_user: messages.error( request, _("The invited user {invited} and your user {current} do not match!").format( invited=invitation.email, current=request.couch_user.username ), ) if request.method == "POST": couch_user = CouchUser.from_django_user(request.user) self._invite(invitation, couch_user) track_workflow( request.couch_user.get_email(), "Current user accepted a project invitation", {"Current user accepted a project invitation": "yes"}, ) meta = get_meta(request) track_existing_user_accepted_invite_on_hubspot.delay(request.couch_user, request.COOKIES, meta) return HttpResponseRedirect(self.redirect_to_on_success) else: mobile_user = CouchUser.from_django_user(request.user).is_commcare_user() context.update( { "mobile_user": mobile_user, "invited_user": invitation.email if request.couch_user.username != invitation.email else "", } ) return render(request, self.template, context) else: if request.method == "POST": form = NewWebUserRegistrationForm(request.POST) if form.is_valid(): # create the new user user = activate_new_user(form) user.save() messages.success(request, _("User account for %s created!") % form.cleaned_data["email"]) self._invite(invitation, user) authenticated = authenticate( username=form.cleaned_data["email"], password=form.cleaned_data["password"] ) if authenticated is not None and authenticated.is_active: login(request, authenticated) track_workflow( request.POST["email"], "New User Accepted a project invitation", {"New User Accepted a project invitation": "yes"}, ) meta = get_meta(request) track_new_user_accepted_invite_on_hubspot.delay(user, request.COOKIES, meta) return HttpResponseRedirect(reverse("domain_homepage", args=[invitation.domain])) else: if CouchUser.get_by_username(invitation.email): return HttpResponseRedirect( reverse("login") + "?next=" + reverse("domain_accept_invitation", args=[invitation.domain, invitation.get_id]) ) domain = Domain.get_by_name(invitation.domain) form = NewWebUserRegistrationForm( initial={ "email": invitation.email, "hr_name": domain.display_name() if domain else invitation.domain, "create_domain": False, } ) context.update({"form": form}) return render(request, self.template, context)
def test_by_domain(self): self.assertEqual(len(Invitation.by_domain('domain_1')), 1) self.assertEqual( len(Invitation.by_domain('domain_1', is_accepted=True)), 2) self.assertEqual(len(Invitation.by_domain('domain_2')), 1) self.assertEqual(len(Invitation.by_domain('domain_3')), 0)
def delete_invitation(request, domain): invitation_id = request.POST['id'] invitation = Invitation.get(invitation_id) invitation.delete() return json_response({'status': 'ok'})
def invitations(self): invitations = Invitation.by_domain(self.domain) for invitation in invitations: invitation.role_label = self.role_labels.get(invitation.role, "") return invitations
def __call__(self, request, invitation_id, **kwargs): logging.info("Don't use this view in more apps until it gets cleaned up.") # add the correct parameters to this instance self.request = request self.inv_id = invitation_id if 'domain' in kwargs: self.domain = kwargs['domain'] if request.GET.get('switch') == 'true': logout(request) return redirect_to_login(request.path) if request.GET.get('create') == 'true': logout(request) return HttpResponseRedirect(request.path) try: invitation = Invitation.get(invitation_id) except ResourceNotFound: messages.error(request, _("Sorry, it looks like your invitation has expired. " "Please check the invitation link you received and try again, or request a " "project administrator to send you the invitation again.")) return HttpResponseRedirect(reverse("login")) if invitation.is_accepted: messages.error(request, _("Sorry, that invitation has already been used up. " "If you feel this is a mistake please ask the inviter for " "another invitation.")) return HttpResponseRedirect(reverse("login")) self.validate_invitation(invitation) if invitation.is_expired: return HttpResponseRedirect(reverse("no_permissions")) # Add zero-width space to username for better line breaking username = self.request.user.username.replace("@", "​@") context = { 'create_domain': False, 'formatted_username': username, 'domain': self.domain, 'invite_to': self.domain, 'invite_type': _('Project'), 'hide_password_feedback': settings.ENABLE_DRACONIAN_SECURITY_FEATURES, } if request.user.is_authenticated: context['current_page'] = {'page_name': _('Project Invitation')} else: context['current_page'] = {'page_name': _('Project Invitation, Account Required')} if request.user.is_authenticated(): is_invited_user = request.couch_user.username.lower() == invitation.email.lower() if self.is_invited(invitation, request.couch_user) and not request.couch_user.is_superuser: if is_invited_user: # if this invite was actually for this user, just mark it accepted messages.info(request, _("You are already a member of {entity}.").format( entity=self.inviting_entity)) invitation.is_accepted = True invitation.save() else: messages.error(request, _("It looks like you are trying to accept an invitation for " "{invited} but you are already a member of {entity} with the " "account {current}. Please sign out to accept this invitation " "as another user.").format( entity=self.inviting_entity, invited=invitation.email, current=request.couch_user.username, )) return HttpResponseRedirect(self.redirect_to_on_success) if not is_invited_user: messages.error(request, _("The invited user {invited} and your user {current} do not match!").format( invited=invitation.email, current=request.couch_user.username)) if request.method == "POST": couch_user = CouchUser.from_django_user(request.user) self._invite(invitation, couch_user) track_workflow(request.couch_user.get_email(), "Current user accepted a project invitation", {"Current user accepted a project invitation": "yes"}) meta = get_meta(request) track_existing_user_accepted_invite_on_hubspot.delay(request.couch_user, request.COOKIES, meta) return HttpResponseRedirect(self.redirect_to_on_success) else: mobile_user = CouchUser.from_django_user(request.user).is_commcare_user() context.update({ 'mobile_user': mobile_user, "invited_user": invitation.email if request.couch_user.username != invitation.email else "", }) return render(request, self.template, context) else: if request.method == "POST": form = WebUserInvitationForm(request.POST) if form.is_valid(): # create the new user user = activate_new_user(form, domain=invitation.domain) user.save() messages.success(request, _("User account for %s created!") % form.cleaned_data["email"]) self._invite(invitation, user) authenticated = authenticate(username=form.cleaned_data["email"], password=form.cleaned_data["password"]) if authenticated is not None and authenticated.is_active: login(request, authenticated) track_workflow(request.POST['email'], "New User Accepted a project invitation", {"New User Accepted a project invitation": "yes"}) meta = get_meta(request) track_new_user_accepted_invite_on_hubspot.delay(user, request.COOKIES, meta) return HttpResponseRedirect(reverse("domain_homepage", args=[invitation.domain])) else: if CouchUser.get_by_username(invitation.email): return HttpResponseRedirect(reverse("login") + '?next=' + reverse('domain_accept_invitation', args=[invitation.domain, invitation.get_id])) form = WebUserInvitationForm(initial={ 'email': invitation.email, 'hr_name': invitation.domain, 'create_domain': False, }) context.update({"form": form}) return render(request, self.template, context)
def setUpClass(cls): super().setUpClass() plan = DefaultProductPlan.get_default_plan_version( edition=SoftwarePlanEdition.ADVANCED) cls.blocked_account = generator.billing_account( '*****@*****.**', '*****@*****.**') cls.blocked_account.block_hubspot_data_for_all_users = True cls.blocked_account.save() # this is one domain linked to the billing account that blocks hubspot cls.blocked_domain = create_domain('block-domain-hubspot') first_blocked_sub = Subscription.new_domain_subscription( cls.blocked_account, cls.blocked_domain.name, plan) first_blocked_sub.is_active = True first_blocked_sub.save() # this is another domain linked to the billing account that blocks hubspot cls.second_blocked_domain = create_domain('block-domain-hubspot-002') second_blocked_sub = Subscription.new_domain_subscription( cls.blocked_account, cls.second_blocked_domain.name, plan) second_blocked_sub.is_active = True second_blocked_sub.save() # this domain is not linked to an account that is blocking hubspot cls.allowed_domain = create_domain('allow-domain-hubspot') allowed_account = generator.billing_account('*****@*****.**', '*****@*****.**') allowed_sub = Subscription.new_domain_subscription( allowed_account, cls.allowed_domain.name, plan) allowed_sub.is_active = True allowed_sub.save() cls.allowed_user = WebUser.create(cls.allowed_domain.name, '*****@*****.**', '*****', None, None) cls.allowed_user.save() cls.blocked_user = WebUser.create(cls.blocked_domain.name, '*****@*****.**', '*****', None, None) cls.blocked_user.save() cls.blocked_couch_user = CouchUser.get_by_username( cls.blocked_user.username) cls.second_blocked_user = WebUser.create( cls.second_blocked_domain.name, '*****@*****.**', '*****', None, None) cls.second_blocked_user.save() cls.second_blocked_couch_user = CouchUser.get_by_username( cls.second_blocked_user.username) cls.blocked_invitation_user = WebUser.create( cls.blocked_domain.name, '*****@*****.**', '*****', None, None) invite_to_blocked_domain = Invitation( email=cls.blocked_invitation_user.username, is_accepted=True, domain=cls.blocked_domain.name, invited_on=datetime.now(), invited_by="*****@*****.**", ) invite_to_blocked_domain.save() cls.blocked_commcare_user = CommCareUser.create( cls.blocked_domain.name, 'testuser', '****', None, None) cls.blocked_commcare_user.save()
def delete_invitation(request, domain): invitation_id = request.POST["id"] invitation = Invitation.get(invitation_id) invitation.delete() return json_response({"status": "ok"})
def test_by_domain(self): self.assertEqual(len(Invitation.by_domain('domain_1')), 1) self.assertEqual(len(Invitation.by_domain('domain_2')), 1) self.assertEqual(len(Invitation.by_domain('domain_3')), 0)
def create_or_update_users_and_groups(upload_domain, user_specs, upload_user, group_memoizer=None, update_progress=None): domain_info_by_domain = {} def _get_domain_info(domain): domain_info = domain_info_by_domain.get(domain) if domain_info: return domain_info if domain == upload_domain: domain_group_memoizer = group_memoizer or GroupMemoizer(domain) else: domain_group_memoizer = GroupMemoizer(domain) domain_group_memoizer.load_all() can_assign_locations = domain_has_privilege(domain, privileges.LOCATIONS) location_cache = None if can_assign_locations: location_cache = SiteCodeToLocationCache(domain) domain_obj = Domain.get_by_name(domain) allowed_group_names = [group.name for group in domain_group_memoizer.groups] roles_by_name = {role.name: role for role in UserRole.by_domain(domain)} domain_user_specs = [spec for spec in user_specs if spec.get('domain', upload_domain) == domain] validators = get_user_import_validators( domain_obj, domain_user_specs, allowed_group_names, list(roles_by_name), upload_domain ) domain_info = DomainInfo( validators, can_assign_locations, location_cache, roles_by_name, domain_group_memoizer ) domain_info_by_domain[domain] = domain_info return domain_info ret = {"errors": [], "rows": []} current = 0 try: for row in user_specs: if update_progress: update_progress(current) current += 1 username = row.get('username') domain = row.get('domain') or upload_domain username = normalize_username(str(username), domain) if username else None status_row = { 'username': username, 'row': row, } domain_info = _get_domain_info(domain) try: for validator in domain_info.validators: validator(row) except UserUploadError as e: status_row['flag'] = str(e) ret['rows'].append(status_row) continue data = row.get('data') email = row.get('email') group_names = list(map(str, row.get('group') or [])) language = row.get('language') name = row.get('name') password = row.get('password') phone_number = row.get('phone-number') uncategorized_data = row.get('uncategorized_data') user_id = row.get('user_id') location_codes = row.get('location_code') or [] if location_codes and not isinstance(location_codes, list): location_codes = [location_codes] # ignore empty location_codes = [code for code in location_codes if code] role = row.get('role', None) web_user = row.get('web_user') try: password = str(password) if password else None is_active = spec_value_to_boolean_or_none(row, 'is_active') is_account_confirmed = spec_value_to_boolean_or_none(row, 'is_account_confirmed') send_account_confirmation_email = spec_value_to_boolean_or_none(row, 'send_confirmation_email') remove_web_user = spec_value_to_boolean_or_none(row, 'remove_web_user') if user_id: user = CommCareUser.get_by_user_id(user_id, domain) if not user: raise UserUploadError(_( "User with ID '{user_id}' not found" ).format(user_id=user_id, domain=domain)) if username and user.username != username: raise UserUploadError(_( 'Changing usernames is not supported: %(username)r to %(new_username)r' ) % {'username': user.username, 'new_username': username}) # note: explicitly not including "None" here because that's the default value if not set. # False means it was set explicitly to that value if is_account_confirmed is False and not web_user: raise UserUploadError(_( "You can only set 'Is Account Confirmed' to 'False' on a new User." )) if is_password(password): user.set_password(password) # overwrite password in results so we do not save it to the db status_row['row']['password'] = '******' status_row['flag'] = 'updated' else: kwargs = {} if is_account_confirmed is not None and not web_user: kwargs['is_account_confirmed'] = is_account_confirmed user = CommCareUser.create(domain, username, password, created_by=upload_user, created_via=USER_CHANGE_VIA_BULK_IMPORTER, commit=False, **kwargs) status_row['flag'] = 'created' if phone_number: user.add_phone_number(_fmt_phone(phone_number), default=True) if name: user.set_full_name(str(name)) if data: user.user_data.update(data) if uncategorized_data: user.user_data.update(uncategorized_data) if language: user.language = language if email: user.email = email.lower() if is_active is not None: user.is_active = is_active if domain_info.can_assign_locations: # Do this here so that we validate the location code before we # save any other information to the user, this way either all of # the user's information is updated, or none of it location_ids = [] for code in location_codes: loc = get_location_from_site_code(code, domain_info.location_cache) location_ids.append(loc.location_id) locations_updated = set(user.assigned_location_ids) != set(location_ids) primary_location_removed = (user.location_id and not location_ids or user.location_id not in location_ids) if primary_location_removed: user.unset_location(commit=False) if locations_updated: user.reset_locations(location_ids, commit=False) if role: role_qualified_id = domain_info.roles_by_name[role].get_qualified_id() user.set_role(domain, role_qualified_id) if web_user: user.user_data.update({'login_as_user': web_user}) user.save() if web_user: if not upload_user.can_edit_web_users(): raise UserUploadError(_( "Only users with the edit web users permission can upload web users" )) current_user = CouchUser.get_by_username(web_user) if remove_web_user: if not current_user or not current_user.is_member_of(domain): raise UserUploadError(_( "You cannot remove a web user that is not a member of this project. {web_user} is not a member.").format(web_user=web_user) ) else: current_user.delete_domain_membership(domain) current_user.save() else: if not role: raise UserUploadError(_( "You cannot upload a web user without a role. {web_user} does not have a role").format(web_user=web_user) ) if not current_user and is_account_confirmed: raise UserUploadError(_( "You can only set 'Is Account Confirmed' to 'True' on an existing Web User. {web_user} is a new username.").format(web_user=web_user) ) if current_user and not current_user.is_member_of(domain) and is_account_confirmed: current_user.add_as_web_user(domain, role=role_qualified_id, location_id=user.location_id) elif not current_user or not current_user.is_member_of(domain): invite_data = { 'email': web_user, 'invited_by': upload_user.user_id, 'invited_on': datetime.utcnow(), 'domain': domain, 'role': role_qualified_id, 'supply_point': user.location_id } invite = Invitation(**invite_data) invite.save() if send_account_confirmation_email: invite.send_activation_email() elif current_user.is_member_of(domain): # edit existing user in the domain current_user.set_role(domain, role_qualified_id) if user.location_id: current_user.set_location(domain, user.location_id) else: current_user.unset_location(domain) current_user.save() if send_account_confirmation_email and not web_user: send_account_confirmation_if_necessary(user) if is_password(password): # Without this line, digest auth doesn't work. # With this line, digest auth works. # Other than that, I'm not sure what's going on # Passing use_primary_db=True because of https://dimagi-dev.atlassian.net/browse/ICDS-465 user.get_django_user(use_primary_db=True).check_password(password) for group in domain_info.group_memoizer.by_user_id(user.user_id): if group.name not in group_names: group.remove_user(user) for group_name in group_names: domain_info.group_memoizer.by_name(group_name).add_user(user, save=False) except (UserUploadError, CouchUser.Inconsistent) as e: status_row['flag'] = str(e) ret["rows"].append(status_row) finally: try: for domain_info in domain_info_by_domain.values(): domain_info.group_memoizer.save_all() except BulkSaveError as e: _error_message = ( "Oops! We were not able to save some of your group changes. " "Please make sure no one else is editing your groups " "and try again." ) logging.exception(( 'BulkSaveError saving groups. ' 'User saw error message "%s". Errors: %s' ) % (_error_message, e.errors)) ret['errors'].append(_error_message) return ret
def __call__(self, request, invitation_id, **kwargs): logging.info( "Don't use this view in more apps until it gets cleaned up.") # add the correct parameters to this instance self.request = request self.inv_id = invitation_id if 'domain' in kwargs: self.domain = kwargs['domain'] if request.GET.get('switch') == 'true': logout(request) return redirect_to_login(request.path) if request.GET.get('create') == 'true': logout(request) return HttpResponseRedirect(request.path) try: invitation = Invitation.get(invitation_id) except ResourceNotFound: messages.error( request, _("Sorry, it looks like your invitation has expired. " "Please check the invitation link you received and try again, or request a " "project administrator to send you the invitation again.")) return HttpResponseRedirect(reverse("login")) if invitation.is_accepted: messages.error( request, _("Sorry, that invitation has already been used up. " "If you feel this is a mistake please ask the inviter for " "another invitation.")) return HttpResponseRedirect(reverse("login")) self.validate_invitation(invitation) if invitation.is_expired: return HttpResponseRedirect(reverse("no_permissions")) # Add zero-width space to username for better line breaking username = self.request.user.username.replace("@", "​@") context = { 'create_domain': False, 'formatted_username': username, 'domain': self.domain, 'invite_to': self.domain, 'invite_type': _('Project'), 'hide_password_feedback': settings.ENABLE_DRACONIAN_SECURITY_FEATURES, } if request.user.is_authenticated: context['current_page'] = {'page_name': _('Project Invitation')} else: context['current_page'] = { 'page_name': _('Project Invitation, Account Required') } if request.user.is_authenticated(): is_invited_user = request.couch_user.username.lower( ) == invitation.email.lower() if self.is_invited(invitation, request.couch_user ) and not request.couch_user.is_superuser: if is_invited_user: # if this invite was actually for this user, just mark it accepted messages.info( request, _("You are already a member of {entity}.").format( entity=self.inviting_entity)) invitation.is_accepted = True invitation.save() else: messages.error( request, _("It looks like you are trying to accept an invitation for " "{invited} but you are already a member of {entity} with the " "account {current}. Please sign out to accept this invitation " "as another user.").format( entity=self.inviting_entity, invited=invitation.email, current=request.couch_user.username, )) return HttpResponseRedirect(self.redirect_to_on_success) if not is_invited_user: messages.error( request, _("The invited user {invited} and your user {current} do not match!" ).format(invited=invitation.email, current=request.couch_user.username)) if request.method == "POST": couch_user = CouchUser.from_django_user(request.user) self._invite(invitation, couch_user) track_workflow( request.couch_user.get_email(), "Current user accepted a project invitation", {"Current user accepted a project invitation": "yes"}) meta = get_meta(request) track_existing_user_accepted_invite_on_hubspot.delay( request.couch_user, request.COOKIES, meta) return HttpResponseRedirect(self.redirect_to_on_success) else: mobile_user = CouchUser.from_django_user( request.user).is_commcare_user() context.update({ 'mobile_user': mobile_user, "invited_user": invitation.email if request.couch_user.username != invitation.email else "", }) return render(request, self.template, context) else: if request.method == "POST": form = WebUserInvitationForm(request.POST) if form.is_valid(): # create the new user user = activate_new_user(form, domain=invitation.domain) user.save() messages.success( request, _("User account for %s created!") % form.cleaned_data["email"]) self._invite(invitation, user) authenticated = authenticate( username=form.cleaned_data["email"], password=form.cleaned_data["password"]) if authenticated is not None and authenticated.is_active: login(request, authenticated) track_workflow( request.POST['email'], "New User Accepted a project invitation", {"New User Accepted a project invitation": "yes"}) meta = get_meta(request) track_new_user_accepted_invite_on_hubspot.delay( user, request.COOKIES, meta) return HttpResponseRedirect( reverse("domain_homepage", args=[invitation.domain])) else: if CouchUser.get_by_username(invitation.email): return HttpResponseRedirect( reverse("login") + '?next=' + reverse('domain_accept_invitation', args=[invitation.domain, invitation.get_id])) form = WebUserInvitationForm( initial={ 'email': invitation.email, 'hr_name': invitation.domain, 'create_domain': False, }) context.update({"form": form}) return render(request, self.template, context)