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)
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)
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')
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"])
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)
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]})