Beispiel #1
0
 def validate(self, attrs):
     attrs = super().validate(attrs=attrs)
     password = attrs["password"]
     if SystemParameter.get(
             "REGISTRATION_SOFT_LOCK") and not password.startswith(
                 SystemParameter.get("REGISTRATION_SOFT_LOCK_KEY")):
         raise ValidationError(_("Registrations are currently locked."),
                               code="registration_locked")
     return attrs
Beispiel #2
0
    def test_create_submission_with_notification(self, notifier_client):
        self.submission_type = SubmissionType.objects.get(name="General")
        self.submission.type = self.submission_type
        self.submission.save()
        payload = {
            "submission_type": "General",
            "submission_status_id": 7,
            "status_context": "received",
        }
        notifier_client().send_email_notification.return_value = {}
        self.client.force_authenticate(user=self.user_1, token=self.user_1.auth_token)
        response = self.post_form(self.url, payload)

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Build footer
        base_footer = SystemParameter.get("NOTIFY_BLOCK_FOOTER")
        email = f"{self.case.reference}@{SystemParameter.get('TRADE_REMEDIES_EMAIL_DOMAIN')}"
        footer = "\n".join([base_footer, f"Contact: {email}"])
        notify_data = {
            "company": self.organisation.name,
            "case_name": self.case.name,
            "case_title": self.case.name,
            "case_number": self.case.reference,
            "case_type": self.case.type.name,
            "investigation_type": self.case.type.name,
            "dumped_or_subsidised": self.case.dumped_or_subsidised(),
            "product": "",
            "full_name": self.user_1.contact.name.strip(),
            "country": "N/A",
            "organisation_name": titlecase(self.organisation.name),
            "notice_url": self.submission.url or "N/A",  # TODO: Remove
            "notice_of_initiation_url": self.submission.url or "N/A",
            "login_url": public_login_url(),
            "submission_type": "General",
            "company_name": titlecase(self.submission.organisation.name)
            if self.submission.organisation
            else "",
            "deadline": "",
            "footer": footer,
            "email": email,
            "guidance_url": SystemParameter.get("LINK_HELP_BOX_GUIDANCE"),
        }
        notifier_client().send_email_notification.assert_called_once_with(
            email_address=self.user_1.email,
            personalisation=notify_data,
            reference=None,
            template_id="d6fb3018-2338-40c9-aa6d-f1195f5f65de",  # /PS-IGNORE
        )
        response_data = response.data["response"]
        self.assertTrue(response_data["success"])
        self.assertEqual(response_data["result"]["submission"]["id"], str(self.submission.id))
        self.assertEqual(response_data["result"]["submission"]["type"]["name"], "General")
Beispiel #3
0
 def post(self, request, template_key, *args, **kwargs):
     template_id = SystemParameter.get(template_key)
     values = request.data.get("values") or {}
     if isinstance(values, str):
         values = json.loads(values)
     template = get_preview(template_id, values)
     return ResponseSuccess({"result": template})
Beispiel #4
0
    def validate(self, attrs: dict) -> dict:
        attrs = super().validate(attrs=attrs)
        password = attrs["password"]
        if SystemParameter.get("REGISTRATION_SOFT_LOCK") and not password.startswith(
            SystemParameter.get("REGISTRATION_SOFT_LOCK_KEY")
        ):
            raise ValidationError(
                _("Registrations are currently locked."), code="registration_locked"
            )

        if attrs.get("code") and attrs.get("case_id") and not attrs.get("confirm_invited_org"):
            raise ValidationError(
                {"organisation_name": _("Organisation name is required.")},
                code="organisation_name_required",
            )

        return attrs
Beispiel #5
0
def get_context(extra_context=None):
    from core.models import SystemParameter

    extra_context = extra_context or {}
    email = notify_contact_email(extra_context.get("case_number"))
    footer = notify_footer(email)
    context = {
        "footer": footer,
        "email": email,
        "guidance_url": SystemParameter.get("LINK_HELP_BOX_GUIDANCE"),
    }
    context.update(extra_context)
    return context
Beispiel #6
0
def notify_footer(email=None):
    """Build notify footer with specified email.

    :param (str) email: contact email for footer.
    :returns (str): NOTIFY_BLOCK_FOOTER system parameter value
      with email appended, if any.
    """
    from core.models import SystemParameter

    footer = SystemParameter.get("NOTIFY_BLOCK_FOOTER")
    if email:
        return "\n".join([footer, f"Contact: {email}"])
    return footer
Beispiel #7
0
def notify_contact_email(case_number=None):
    """Build notify email address.

    If a case is specified build contact email with it.

    :param (str) case_number: e.g. 'TD0001'
    :returns (str): A case contact email if case number specified, otherwise
      value of TRADE_REMEDIES_EMAIL system parameter.
    """
    from core.models import SystemParameter

    if case_number:
        return f"{case_number}@{SystemParameter.get('TRADE_REMEDIES_EMAIL_DOMAIN')}"
    return SystemParameter.get("TRADE_REMEDIES_EMAIL")
Beispiel #8
0
 def handle(self, *args, **options):
     logger.info("Loading system parameters")
     path = options.get("path")
     if not path:
         path = os.path.join(
             pathlib.Path(__file__).parent.parent.parent, "system",
             "parameters.json")
     with open(path) as json_data:
         objects = json.loads(str(json_data.read()))
     count_created, count_updated, count_removed = SystemParameter.load_parameters(
         objects)
     if count_updated:
         logger.info(f"Upadted {count_updated} row(s)")
     if count_created:
         logger.info(f"Created {count_created} row(s)")
     if count_removed:
         logger.info(f"Removed {count_removed} rows(s)")
def is_enabled(name):
    """
    Evaluate if a feature is enabled.

    Feature flags are expected to be defined as SystemParameter objects of
    type `int` with a key that is upper case and prefixed with `FEATURE_`.
    A value of 1 implies the feature is enabled.
    A value of 0 implies the feature is disabled.
    """
    key = f"FEATURE_{name.upper()}"
    cache_key = f"FF:{key}"
    val = cache.get(cache_key)
    if val is None:
        sys_param = SystemParameter.get(key)
        if sys_param is None:
            raise FeatureFlagNotFound(key)
        val = sys_param > 0
        cache.set(cache_key, val, feature_flag_ttl)
    return val
Beispiel #10
0
    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
        """
        Arguments:
            request: a Django Request object
        Returns:
            ResponseSuccess response with the user's token and user data
            ResponseError response if the user could not be created  #todo - raise an error like the other views
        """
        registration_data = json.loads(request.data["registration_data"])
        serializer = V2RegistrationSerializer(data=registration_data)
        if serializer.is_valid():
            serializer.save()
            return ResponseSuccess({"result": serializer.data},
                                   http_status=status.HTTP_201_CREATED)
        else:
            if "User already exists." in serializer.errors.get("email",
                                                               []).detail:
                # If the email already exists,
                # notify the original user and pretend registration completed ok.
                user = User.objects.get(
                    email__iexact=serializer.initial_data["email"])
                template_id = SystemParameter.get("NOTIFY_EMAIL_EXISTS")
                send_mail(user.email, {"full_name": user.name}, template_id)

                return ResponseSuccess(
                    {
                        "result": {
                            "email": serializer.initial_data["email"],
                            "pk": uuid.uuid4(),  # Give them a random UUID
                        }
                    },
                    http_status=status.HTTP_201_CREATED,
                )
            else:
                raise ValidationAPIException(
                    serializer_errors=serializer.errors)
Beispiel #11
0
 def test_dict_value(self):
     value = SystemParameter.get("DICT")
     assert isinstance(value, dict)
     assert value["one"] == 1
     assert value["two"] == 2
Beispiel #12
0
 def test_string_value(self):
     value = SystemParameter.get("STRING")
     assert value == "str_value"
Beispiel #13
0
 def test_list_of_string(self):
     value = SystemParameter.get("STRING_LIST")
     assert len(value) == 2
     assert isinstance(value[0], str)
     assert value[1] == "two"
Beispiel #14
0
 def test_list_with_content_type(self):
     value = SystemParameter.get("MODELS")
     assert len(value) == 2
     assert isinstance(value[0], Document)
Beispiel #15
0
    def send(self, sent_by, context=None, direct=False, template_key=None):
        """Send the invite email via notify

        Arguments:
            sent_by {User} -- The user sending the invitation

        Keyword Arguments:
            context {dict} -- extra context dict (default: {None})
            direct {bool} -- include a direct login link with the invite codes (default: {False})
            template_key {str} -- The system param pointing to the template id (default: {None})

        Raises:
            InvitationFailure: raises if the invite is lacking a contact reference
        """
        if not self.contact:
            raise InvitationFailure("No contact to invite")
        template_key = template_key or "NOTIFY_INFORM_INTERESTED_PARTIES"
        notify_template_id = SystemParameter.get(template_key)
        _context = {
            "organisation_name": self.organisation.name,
            "company_name": self.organisation.name,
            "full_name": self.contact.name,  # invited contact
            "login_url": f"{settings.PUBLIC_ROOT_URL}",
            "guidance_url": SystemParameter.get("LINK_HELP_BOX_GUIDANCE"),
            "invited_by": self.created_by.name,
        }
        if self.case:
            product = self.case.product_set.first()
            export_source = self.case.exportsource_set.first()
            case_name = self.case.name or (product.sector.name
                                           if product else "N/A")
            _context.update({
                "case_name":
                case_name,
                "case_number":
                self.case.reference,
                "investigation_type":
                self.case.type.name,
                "dumped_or_subsidised":
                self.case.dumped_or_subsidised(),
                "product":
                product.name,
                "country":
                export_source.country.name if export_source else None,
                "notice_url":
                self.submission.url if self.submission else "",  # TODO: Remove
                "notice_of_initiation_url":
                self.case.latest_notice_of_initiation_url,
                "invited_by_name":
                self.submission.contact.name if self.submission else "",
                "invited_by_organisation":
                self.submission.organisation.name if self.submission else "",
            })
        # Set email and footer appropriate to case context
        email = notify_contact_email(_context.get("case_number"))
        _context.update({"email": email, "footer": notify_footer(email)})
        if direct is True:
            _context[
                "login_url"] = f"{settings.PUBLIC_ROOT_URL}/invitation/{self.code}/{self.case.id}/"
        if context:
            _context.update(context)

        audit_kwargs = {
            "audit_type": AUDIT_TYPE_NOTIFY,
            "user": sent_by,
            "case": self.case,
            "model": self.contact,
        }
        send_mail(self.contact.email,
                  _context,
                  notify_template_id,
                  audit_kwargs=audit_kwargs)
Beispiel #16
0
    def merge_organisation_records(self,
                                   organisation,
                                   merge_with=None,
                                   parameter_map=None,
                                   merged_by=None,
                                   notify=False):
        """
        Merge two organisations records into one.
        parameter_map is a map of fields that need to be copied from the merge_with object
        """
        from contacts.models import Contact
        from invitations.models import Invitation

        results = []
        results.append(
            Submission.objects.filter(organisation=merge_with).update(
                organisation=organisation))
        results.append(
            Contact.objects.filter(organisation=merge_with).update(
                organisation=organisation))
        results.append(
            Invitation.objects.filter(organisation=merge_with).update(
                organisation=organisation))
        results.append(
            OrganisationName.objects.filter(organisation=merge_with).update(
                organisation=organisation))
        # transfer usercases after finding clashes
        sql = f"""select uc2.id from security_usercase uc1 join security_usercase uc2
            on uc1.organisation_id='{organisation.id}'
            and uc2.organisation_id = '{merge_with.id}'
            and uc1.case_id=uc2.case_id and uc1.user_id=uc2.user_id
            """
        clash_list = sql_get_list(sql)
        try:
            results.append(
                UserCase.objects.filter(organisation=merge_with).exclude(
                    id__in=clash_list).update(organisation=organisation))
        except Exception as e:
            raise ValueError(
                "Same user has access to same case on behalf of both organisations"
            )

        # transfer case_org_contacts after finding clashes
        sql = f"""select cc2.id from contacts_casecontact cc1 join contacts_casecontact cc2
            on cc1.organisation_id='{organisation.id}'
            and cc2.organisation_id = '{merge_with.id}'
            and cc1.case_id=cc2.case_id and cc1.contact_id=cc2.contact_id
            """
        clash_list = sql_get_list(sql)
        try:
            results.append(
                CaseContact.objects.filter(organisation=merge_with).exclude(
                    id__in=clash_list).update(organisation=organisation))
        except Exception as e:
            raise ValueError("Same contact is in both organisations")

        # Migrate cases (caseroles)
        clash_cases = {}
        for org_case in OrganisationCaseRole.objects.filter(
                organisation=organisation):
            clash_cases[org_case.case.id] = org_case
        for org_case in OrganisationCaseRole.objects.filter(
                organisation=merge_with):
            clash = clash_cases.get(org_case.case.id)
            if clash:
                if org_case.role.key not in NOT_IN_CASE_ORG_CASE_ROLES:
                    if (clash.role.key not in NOT_IN_CASE_ORG_CASE_ROLES
                            and org_case.role.key != clash.role.key):
                        # Argh, both orgs are in the same case with different,
                        # non awaiting roles - blow up!
                        raise ValueError(
                            "Cannot merge as organisations have different roles in a case",
                            org_case.case.name,
                        )
                    # Pick the best possible role for the merged org
                    clash.role = org_case.role
                    clash.save()
                clash_cases[org_case.case.id] = org_case
                org_case.delete()
        results.append(
            OrganisationCaseRole.objects.filter(
                organisation=merge_with).update(organisation=organisation))

        results.append(
            OrganisationUser.objects.filter(organisation=merge_with).update(
                organisation=organisation))
        updated = False
        for parameter, source in parameter_map.items():
            if source == "p2":
                setattr(organisation, parameter,
                        getattr(merge_with, parameter))
                updated = True
        if updated:
            organisation.merged_from = merge_with
            organisation.save()
        # Notify the admins of the organisation of the merge.
        if notify and merged_by:
            notify_template_id = SystemParameter.get(
                "NOTIFY_ORGANISATION_MERGED")
            # any baseline context can be set here.
            context = {
                "footer": notify_footer(notify_contact_email()),
                "public_cases": SystemParameter.get("LINK_TRA_CASELIST"),
            }
            self.notify_owners(
                organisation=organisation,
                template_id=notify_template_id,
                context=context,
                notified_by=merged_by,
            )
        # soft delete the merged organisation
        merge_with.delete()
        return results
Beispiel #17
0
                    return contacts[0]
        return contact

    def notify_approval_status(self, action, contact, values, case, sent_by):
        """
        Notify organisation contact about an approval or rejection to a case.
        """
        templates_map = {
            "approve": "NOTIFY_INTERESTED_PARTY_REQUEST_PERMITTED",
            "deny": "NOTIFY_INTERESTED_PARTY_REQUEST_DENIED",
            "change": "NOTIFY_COMPANY_ROLE_CHANGED_V2",
            "remove": "NOTIFY_COMPANY_ROLE_DENIED_V2",
        }
        template_id = templates_map.get(action)
        if template_id:
            notify_template_id = SystemParameter.get(template_id)
            audit_kwargs = {
                "audit_type": AUDIT_TYPE_NOTIFY,
                "user": sent_by,
                "case": case,
                "model": contact,
            }
            send_mail(contact.email,
                      values,
                      notify_template_id,
                      audit_kwargs=audit_kwargs)
        else:
            logger.error("Invalid action for organisation notification: %s",
                         action)

    def has_previous_names(self):
Beispiel #18
0
    def notify(self,
               sent_by,
               contact=None,
               context=None,
               template_id=None,
               new_status=None):
        """
        Notify the contact about this submission using the given template

        :param core.User sent_by: The user performing an action on the submission.
        :param contacts.Contact contact: An optional contact to be notified.
            Defaults to the submission's designated contact.
        :param dict context: An optional dictionary of parameters to be made
            available to the template.
        :param str template_id: An optional string representing the key of a
            Notify template in the System Parameters.
            Defaults to NOTIFY_QUESTIONNAIRE
        :param str new_status: An optional status that the submission will be
            moved to after sending the notification.
            This value should correspond to the property of the SubmissionType
            excluding the `_status` suffix eg:
                - `sent` -> self.type.sent_status
                - `received` -> self.type.received_status
            Defaults to None ie the submission status will not change.
        """
        contact = contact or self.contact
        template_id = template_id or "NOTIFY_QUESTIONNAIRE"
        if template_id == "NOTIFY_APPLICATION_SUCCESSFUL":
            template_id = "NOTIFY_APPLICATION_SUCCESSFUL_V2"
        notify_template_id = SystemParameter.get(template_id)
        export_sources = self.case.exportsource_set.filter(
            deleted_at__isnull=True)
        export_countries = [src.country.name for src in export_sources]
        product = self.case.product_set.first()
        case_name = self.case.name
        company_name = titlecase(self.organisation.name)
        values = {
            "company":
            self.organisation.name,
            "investigation_type":
            self.case.type.name,
            "product":
            product.name if product else "",
            "case_name":
            case_name,
            "case_number":
            self.case.reference,
            "full_name":
            contact.name.strip() if contact else "N/A",
            "country":
            ", ".join(export_countries) if export_countries else "N/A",
            "organisation_name":
            company_name,
            "company_name":
            company_name,
            "login_url":
            public_login_url(),
            "submission_type":
            self.type.name,
            "deadline":
            self.due_at.strftime(settings.FRIENDLY_DATE_FORMAT)
            if self.due_at else "",
            "dumped_or_subsidised":
            self.case.dumped_or_subsidised(),
            "case_title":
            case_name,  # TODO: merge the two identicals
            "notice_url":
            self.case.latest_notice_of_initiation_url,  # TODO: remove
            "notice_of_initiation_url":
            self.case.latest_notice_of_initiation_url,
        }

        if context:
            if template_id == "NOTIFY_QUESTIONNAIRE":
                context[
                    "footer"] = "Investigations Team\r\nTrade Remedies\r\nDepartment for International Trade"  # /PS-IGNORE
            values.update(context)
        if template_id == "NOTIFY_AD_HOC_EMAIL":
            values[
                "footer"] = "Investigations Team\r\nTrade Remedies\r\nDepartment for International Trade\r\nContact: [email protected]"  # /PS-IGNORE

        audit_kwargs = {
            "audit_type": AUDIT_TYPE_NOTIFY,
            "user": sent_by,
            "case": self.case,
            "model": contact,
        }
        send_mail(contact.email,
                  values,
                  notify_template_id,
                  audit_kwargs=audit_kwargs)
        if new_status:
            self.status = getattr(self.type, f"{new_status}_status")
            if new_status == "sent":
                self.sent_at = timezone.now()
                self.set_due_date()
            self.save()
Beispiel #19
0
 def get(self, request, template_key, *args, **kwargs):
     template_id = SystemParameter.get(template_key)
     template = get_template(template_id)
     return ResponseSuccess({"result": template})
Beispiel #20
0
class AssignUserToCaseView(TradeRemediesApiView):
    def get(self, request, organisation_id, user_id=None):
        from cases.models import Submission

        organisation = Organisation.objects.get(id=organisation_id)
        if user_id:
            user = User.objects.get(id=user_id)
            contact_ids = [user.contact.id]
        else:
            users = organisation.users
            contact_ids = users.values_list("user__userprofile__contact",
                                            flat=True)
        submissions = Submission.objects.filter(
            (Q(status__draft=True) | Q(status__received=True)),
            contact_id__in=contact_ids,
            created_by__in=users.values_list("user", flat=True),
            type__key="assign",
        )
        return ResponseSuccess(
            {"results": [submission.to_dict() for submission in submissions]})

    @transaction.atomic
    def post(
        self,
        request,
        organisation_id,
        user_id,
        case_id,
        representing_id=None,
        submission_id=None,
        invite_id=None,
    ):
        from cases.models import get_case

        primary = request.data.get("primary")
        remove = request.data.get("remove")
        try:
            user_organisation = Organisation.objects.get(id=organisation_id)
            if representing_id:
                representing = Organisation.objects.get(id=representing_id)
            else:
                representing = user_organisation
        except Organisation.DoesNotExist:
            raise NotFoundApiExceptions("Invalid parameters or access denied")
        if not request.user.is_tra() and user_id and (user_id !=
                                                      request.user.id):
            if not request.user.groups.filter(
                    name=SECURITY_GROUP_ORGANISATION_OWNER).exists():
                raise InvalidAccess(
                    "Only organisation owners can update other members")
        case = get_case(case_id)
        user = User.objects.get(
            id=user_id, organisationuser__organisation=user_organisation)
        if not remove:
            user.assign_to_case(case=case,
                                organisation=representing,
                                created_by=request.user)
            user.contact.add_to_case(
                case=case,
                organisation=representing,
                primary=bool(primary),
            )
            context = {
                "case_name": case.name,
                "case_number": case.reference,
                "company_name": user_organisation.name,
                "representing_clause": f" representing {representing.name}",
                "login_url": public_login_url(),
            }
            context[
                "footer"] = "Investigations Team\r\nTrade Remedies\r\nDepartment for International Trade"  # /PS-IGNORE
            context["full_name"] = user.contact.name or user.name
            audit_kwargs = {
                "audit_type": AUDIT_TYPE_NOTIFY,
                "case": case,
                "user": user,
                "model": user.contact,
            }
            send_mail(
                email=user.contact.email,
                context=context,
                template_id=SystemParameter.get(
                    "NOTIFY_USER_ASSIGNED_TO_CASE"),
                audit_kwargs=audit_kwargs,
            )
Beispiel #21
0
def load_system_params():
    with open(os.path.join(Path(settings.BASE_DIR).parent.absolute(), "core/system/parameters.json")) as json_data:
        objects = json.loads(str(json_data.read()))
    return SystemParameter.load_parameters(objects)
Beispiel #22
0
    def notify_deficiency(self,
                          sent_by,
                          contact=None,
                          context=None,
                          template_id=None):
        """
        Notify the contact about a deficiency to this submission using the given template.
        If no template is provided, the type's default is used falling
        back to the default deficiency template.
        """
        contact = contact or self.contact
        template_id = "NOTIFY_SUBMISSION_DEFICIENCY"
        if context.get("submission_type", "") == "Application":
            template_id = "NOTIFY_APPLICATION_INSUFFICIENT_V2"
        notify_template_id = SystemParameter.get(template_id)
        product = self.case.product_set.first()
        product_name = product.name if product else ""
        case_name = self.case.name
        company_name = titlecase(self.organisation.name)
        # set the due date on this submission
        self.set_due_date(force=True)
        values = {
            "company":
            company_name,
            "investigation_type":
            self.case.type.name,
            "product":
            product_name,
            "full_name":
            contact.name.strip() if contact else "N/A",
            "organisation_name":
            company_name,
            "case_number":
            self.case.reference,
            "case_name":
            case_name,
            "tra_contact_name":
            "us",
            "submission_type":
            self.type.name,
            "login_url":
            public_login_url(),
            "deadline":
            self.due_at.strftime(settings.FRIENDLY_DATE_FORMAT)
            if self.due_at else "N/A",
        }
        if context:
            values.update(context)
        audit_kwargs = {
            "audit_type": AUDIT_TYPE_NOTIFY,
            "user": sent_by,
            "case": self.case,
            "model": contact,
        }
        send_mail(contact.email,
                  values,
                  notify_template_id,
                  audit_kwargs=audit_kwargs)

        self.deficiency_sent_at = timezone.now()
        self.sent_at = timezone.now()
        self.save()
Beispiel #23
0
                else:
                    groups.append(SECURITY_GROUP_ORGANISATION_OWNER)
                user = serializer.save(groups=groups, **contact_kwargs)
                invitation.process_invitation(
                    user, accept=accept, register_interest=register_interest)
            else:
                user = serializer.save()

            return ResponseSuccess({"result": user.to_dict()},
                                   http_status=status.HTTP_201_CREATED)
        else:
            if serializer.errors.get("email", []) == ["User already exists."]:
                # If the email already exists,
                # notify the original user and pretend registration completed ok.
                user = serializer.get_user(serializer.initial_data["email"])
                template_id = SystemParameter.get("NOTIFY_EMAIL_EXISTS")
                send_mail(user.email, {"full_name": user.name}, template_id)

                return ResponseSuccess(
                    {
                        "result": {
                            "email": serializer.initial_data["email"],
                            "id": None,
                        }
                    },
                    http_status=status.HTTP_201_CREATED,
                )
            return ResponseError(serializer.errors)


class TwoFactorRequestAPI(TradeRemediesApiView):