Exemplo n.º 1
0
 def create(cls, *args, **kwargs):  # pylint: disable=arguments-differ
     # OpenEdXInstance constructor accepts either a 'sub_domain' or 'instance_lms_domain' value. Only generate a
     # random value for 'internal_lms_domain' if neither 'sub_domain' nor 'internal_lms_domain' are provided.
     if 'sub_domain' not in kwargs and 'internal_lms_domain' not in kwargs:
         kwargs = kwargs.copy()
         random_id = str(uuid.uuid4())[:8]
         sub_domain = 'instance{}.test'.format(random_id)
         kwargs['internal_lms_domain'] = generate_internal_lms_domain(
             sub_domain)
         kwargs['extra_custom_domains'] = (
             "custom1.{lms_domain}\r\n"
             "custom2.{lms_domain}".format(
                 lms_domain=generate_internal_lms_domain(sub_domain)))
     return super(OpenEdXInstanceFactory, cls).create(*args, **kwargs)
Exemplo n.º 2
0
    def clean(self):
        """
        Verify that the domains were not already been taken by any running instance.

        We can't do this in a regular validator, since we have to allow the subdomain of the
        instance associated with this application.
        """
        errors = {}

        # Check internal domain
        generated_domain = generate_internal_lms_domain(self.subdomain)
        if self.instance is not None and self.instance.internal_lms_domain == generated_domain:
            return
        if OpenEdXInstance.objects.filter(
                internal_lms_domain=generated_domain).exists():
            subdomain_error = ValidationError(
                message='This domain is already taken.',
                code='unique',
            )
            errors['subdomain'] = [subdomain_error]

        # Check external domain, if present
        if self.external_domain:
            if self.instance is not None and self.instance.external_lms_domain == self.external_domain:
                return
            if OpenEdXInstance.objects.filter(
                    external_lms_domain=self.external_domain).exists():
                external_domain_error = ValidationError(
                    message='This domain is already taken.',
                    code='unique',
                )
                errors['external_domain'] = [external_domain_error]

        if errors:
            raise ValidationError(errors)
Exemplo n.º 3
0
def validate_available_subdomain(value):
    """
    Prevent users from registering with a subdomain which is in use.

    The validation reduces the risk of security issues when someone is trying to take over
    control of a client resource (domain) if they forget to restrict its access.
    """
    if is_subdomain_contains_reserved_word(value):
        raise ValidationError(
            message=f'Cannot register domain starting with "{value}".',
            code='reserved')

    # if subdomain_exists return instead of raising validation error, because the unique
    # check already raises the error
    generated_domain = generate_internal_lms_domain(value)
    is_subdomain_registered = BetaTestApplication.objects.filter(
        subdomain=value).exists()
    is_assigned_to_instance = OpenEdXInstance.objects.filter(
        internal_lms_domain=generated_domain).exists()

    if is_subdomain_registered or is_assigned_to_instance:
        raise ValidationError(message='This domain is already taken.',
                              code='unique')

    managed_domains = set([
        settings.DEFAULT_INSTANCE_BASE_DOMAIN,
        settings.GANDI_DEFAULT_BASE_DOMAIN,
    ])

    for domain in managed_domains:
        try:
            records = gandi_api.filter_dns_records(domain)
            records = {
                tldextract.extract(record["name"])
                for record in records
            }
        except Exception as exc:
            logger.warning('Unable to retrieve the domains for %s: %s.',
                           domain, str(exc))
            raise ValidationError(message='The domain cannot be validated.',
                                  code='cannot_validate')

        # Because registered CNAMEs may have multiple dots (.) in their subdomain
        # we need to check for the starting and ending part of it.
        # Ex: haproxy-integration.my.net.opencraft.hosting is registered, but we must reject
        # registrations for net.opencraft.hosting and haproxy-integration.my.net as well.
        registered_subdomains = set()
        for dns_record in records:
            registered_subdomains.update([
                dns_record.subdomain,  # the whole subdomain
                dns_record.subdomain.split(".")[
                    -1]  # base of the subdomain like .net.*
            ])

        subdomain_base = value.split(".")[-1]
        if value in registered_subdomains or subdomain_base in registered_subdomains:
            raise ValidationError(message='This domain is already taken.',
                                  code='unique')
Exemplo n.º 4
0
    def update_instance_from_pr(self, pr):
        """
        Update/create the associated sandbox instance with settings from the given pull request.

        This will not spawn a new AppServer.
        This method will automatically save this WatchedPullRequest's 'instance' field.
        """
        # The following fields should never change:
        assert self.github_pr_url == pr.github_pr_url
        assert self.fork_name == pr.fork_name
        assert self.branch_name == pr.branch_name
        # Create an instance if necessary:
        instance = self.instance or OpenEdXInstance()
        instance.internal_lms_domain = generate_internal_lms_domain('pr{number}.sandbox'.format(number=pr.number))
        instance.edx_platform_repository_url = self.repository_url
        instance.edx_platform_commit = self.get_branch_tip()
        instance.name = (
            'PR#{pr.number}: {pr.truncated_title} ({pr.username}) - {i.reference_name} ({commit_short_id})'
            .format(pr=pr, i=self, commit_short_id=instance.edx_platform_commit[:7])
        )
        instance.configuration_extra_settings = yaml_merge(
            self.watched_fork.configuration_extra_settings,
            pr.extra_settings
        )
        if not instance.ref.creator or not instance.ref.owner:
            user = UserProfile.objects.get(github_username=pr.username)
            instance.ref.creator = user
            instance.ref.owner = user.organization
        # Configuration repo and version and edx release follow this precedence:
        # 1) PR settings. 2) WatchedFork settings. 3) instance model defaults
        instance.configuration_source_repo_url = pr.get_extra_setting(
            'edx_ansible_source_repo',
            default=(
                self.watched_fork.configuration_source_repo_url or
                instance.configuration_source_repo_url
            )
        )
        instance.configuration_version = pr.get_extra_setting(
            'configuration_version', default=(
                self.watched_fork.configuration_version or
                instance.configuration_version
            )
        )
        instance.openedx_release = pr.get_extra_setting(
            'openedx_release', default=(
                self.watched_fork.openedx_release or
                instance.openedx_release
            )
        )
        # Save atomically. (because if the instance gets created but self.instance failed to
        # update, then any subsequent call to update_instance_from_pr() would try to create
        # another instance, which would fail due to unique domain name constraints.)
        with transaction.atomic():
            instance.save()
            if not self.instance:
                self.instance = instance
                self.save(update_fields=["instance"])
Exemplo n.º 5
0
 def create(cls, *args, **kwargs):
     # OpenEdXInstance constructor accepts either a 'sub_domain' or 'instance_lms_domain' value. Only generate a
     # random value for 'internal_lms_domain' if neither 'sub_domain' nor 'internal_lms_domain' are provided.
     if 'sub_domain' not in kwargs and 'internal_lms_domain' not in kwargs:
         kwargs = kwargs.copy()
         random_id = str(uuid.uuid4())[:8]
         sub_domain = 'instance{}.test'.format(random_id)
         kwargs['internal_lms_domain'] = generate_internal_lms_domain(
             sub_domain)
     return super(OpenEdXInstanceFactory, cls).create(*args, **kwargs)
Exemplo n.º 6
0
    def clean(self):
        """
        Verify that the subdomain has not already been taken by any running instance.

        We can't do this in a regular validator, since we have to allow the subdomain of the
        instance associated with this application.
        """
        generated_domain = generate_internal_lms_domain(self.subdomain)
        if self.instance is not None and self.instance.internal_lms_domain == generated_domain:
            return
        if OpenEdXInstance.objects.filter(
                internal_lms_domain=generated_domain).exists():
            subdomain_error = ValidationError(
                message='This domain is already taken.',
                code='unique',
            )
            raise ValidationError({'subdomain': [subdomain_error]})