Esempio n. 1
0
class UnsafeJSEvalMixin(object):
    @method_decorator(
        csp_update(
            # jquery.datetimepicker.min.js requires javascript eval
            SCRIPT_SRC=("'unsafe-eval'", ), ))
    def dispatch(self, *args, **kwargs):
        return super(UnsafeJSEvalMixin, self).dispatch(*args, **kwargs)
Esempio n. 2
0
 def get_urls(self):
     return [
         path(
             "dashboard/",
             self.site.admin_view(
                 # Argh, it's really unfortunate that we have to break CSP
                 # in order to use Vega, which apparently uses eval(). :(
                 #
                 # But, since our datasets never contain arbitrary string data
                 # entered by untrusted users, this *should* be ok.
                 csp_update(SCRIPT_SRC="'unsafe-eval'")
                 (self.dashboard_view)),
             name="dashboard"),
     ]
Esempio n. 3
0
def embeddable_in_frontapp(view):
    embeddable_view = csp_update(FRAME_ANCESTORS=[
        "https://*.frontapp.com",
        "https://*.frontapplication.com",
    ])(view)

    @functools.wraps(view)
    def wrapped_view(request, *args, **kwargs):
        v = view
        if does_url_have_auth_secret(request.get_full_path(),
                                     settings.FRONTAPP_PLUGIN_AUTH_SECRET):
            v = embeddable_view
        return v(request, *args, **kwargs)

    return wrapped_view
Esempio n. 4
0
def csp_fixes(view):
    CSP_REPLACEMENTS = {
        'SCRIPT_SRC': (
            "'self'",
            "'unsafe-inline'",
        ),
    }

    CSP_ADDITIONS = {
        'STYLE_SRC': (
            # float: left
            "'sha256-e+Z0n8P0IwqIce2RMye3/p5TaNb2k/QdJT4urKCsrwk='",
            # clear: both
            "'sha256-matwEc6givhWX0+jiSfM1+E5UMk8/UGLdl902bjFBmY='",
        ),
    }

    view = csp_replace(**CSP_REPLACEMENTS)(view)
    view = csp_update(**CSP_ADDITIONS)(view)
    return view
Esempio n. 5
0
    app_description, AppReleasesView, AppUploadView, AppRatingApi, \
    AppRegisterView
from nextcloudappstore.scaffolding.views import AppScaffoldingView

admin.site.login = login_required(admin.site.login)

urlpatterns = [
    url(r'^$', CategoryAppListView.as_view(), {'id': None}, name='home'),
    url(r"^featured$",
        CategoryAppListView.as_view(), {
            'id': None,
            'is_featured_category': True
        },
        name='featured'),
    url(r"^signup/$",
        csp_update(**settings.CSP_SIGNUP)(signup),
        name="account_signup"),
    url(r"^social/signup/$",
        csp_update(**settings.CSP_SIGNUP)(social_signup),
        name="socialaccount_signup"),
    url(r'^', include('allauth.urls')),
    url(r'^categories/(?P<id>[\w]*)/?$',
        CategoryAppListView.as_view(),
        name='category-app-list'),
    url(r'^developer/apps/generate/?$',
        AppScaffoldingView.as_view(),
        name='app-scaffold'),
    url(r'^developer/apps/releases/new/?$',
        AppUploadView.as_view(),
        name='app-upload'),
    url(r'^developer/apps/new/?$',
Esempio n. 6
0
class CreateReportChoiceEntryPoint(TemplateView):
    template_name = 'newreportchoice.html'

    def get_context_data(self, **kwargs):
        context = super(CreateReportChoiceEntryPoint,
                        self).get_context_data(**kwargs)
        context['type'] = {
            'report': ContentType.objects.get_for_model(Report).id,
            'onlinereport': ContentType.objects.get_for_model(OnlineReport).id
        }
        context['authenticated'] = self.request.user.is_authenticated
        return context


@method_decorator(csp_update(STYLE_SRC=("'unsafe-inline'", )), name='dispatch')
class CreateReportView(FormView):
    """
    Creates a new report and returns the new report's ID
    """
    form_class = NewReportForm
    template_name = "newreport.html"

    def get_context_data(self, **kwargs):
        context = super(CreateReportView, self).get_context_data(**kwargs)
        context['type'] = {
            'report': ContentType.objects.get_for_model(Report).id,
            'onlinereport': ContentType.objects.get_for_model(OnlineReport).id
        }
        context['this_type'] = "report"
        context['mode'] = 'new'
Esempio n. 7
0
                'end': current_date + timedelta(days=1),
                'first_start': min([t.start for t in talks if t.start and t.start.astimezone(tz).date() == current_date.date()] or [0]),
                'last_end': max([t.end for t in talks if t.start and t.start.astimezone(tz).date() == current_date.date()] or [0]),
                'rooms': [{
                    'name': room.name,
                    'talks': [talk for talk in talks
                              if talk.start and talk.start.astimezone(tz).date() == current_date.date() and talk.room_id == room.pk],
                } for room in rooms],
            } for index, current_date in enumerate([
                event.datetime_from + timedelta(days=i) for i in range((event.date_to - event.date_from).days + 1)
            ])
        ]
        return ctx


@method_decorator(csp_update(STYLE_SRC="'self' 'unsafe-inline'"), name='dispatch')
class ScheduleView(PermissionRequired, ScheduleDataView):
    template_name = 'agenda/schedule.html'
    permission_required = 'agenda.view_schedule'

    def get_permission_object(self):
        return self.request.event

    def get_object(self):
        if self.version == 'wip' and self.request.user.has_perm('orga.view_schedule', self.request.event):
            return self.request.event.wip_schedule
        return super().get_object()

    def get_context_data(self, *args, **kwargs):
        ctx = super().get_context_data(*args, **kwargs)
        tz = pytz.timezone(self.request.event.timezone)
Esempio n. 8
0
                        _('Yay, your changes have been saved and the connection attempt to '
                          'your SMTP server was successful.'))
                else:
                    messages.success(
                        self.request,
                        _('We\'ve been able to contact the SMTP server you configured. '
                          'Remember to check the "use custom SMTP server" checkbox, '
                          'otherwise your SMTP server will not be used.'))
        else:
            messages.success(self.request, _('Yay! We saved your changes.'))

        ret = super().form_valid(form)
        return ret


@method_decorator(csp_update(SCRIPT_SRC="'self' 'unsafe-inline'"), name='get')
class EventTeam(EventSettingsPermission, TemplateView):
    template_name = 'orga/settings/team.html'

    @cached_property
    def formset(self):
        formset_class = forms.inlineformset_factory(
            Event,
            EventPermission,
            can_delete=True,
            extra=0,
            fields=[
                'is_orga',
                'is_reviewer',
                'review_override_count',
                'invitation_email',
Esempio n. 9
0
from django.conf.urls import url
from djangae.utils import djangae_webapp

from django.views.decorators.csrf import csrf_exempt

# The Mapreduce status UI uses inline JS, which will fail If we have django-csp
# installed and are not allowing 'unsafe-inline' as a SCRIPT_SRC.
try:
    from csp.decorators import csp_update
    exempt_from_unsafe_inline = csp_update(SCRIPT_SRC=("'unsafe-inline'",))
except ImportError:
    exempt_from_unsafe_inline = lambda func: func


try:
    from mapreduce.main import create_handlers_map
    wrapped_urls = [
        url(
            url_re.replace('.*/', '^', 1), 
            exempt_from_unsafe_inline(csrf_exempt(djangae_webapp(func)))
        ) 
        for url_re, func in create_handlers_map()
    ]
except ImportError as e:
    wrapped_urls = []


urlpatterns = wrapped_urls
Esempio n. 10
0
            elif self.request.GET["role"] == "false":
                qs = qs.exclude(
                    user__submissions__in=self.request.event.submissions.filter(
                        state__in=[
                            SubmissionStates.ACCEPTED,
                            SubmissionStates.CONFIRMED,
                        ]
                    )
                )

        qs = qs.order_by("id").distinct()
        qs = self.sort_queryset(qs)
        return qs


@method_decorator(csp_update(IMG_SRC="https://www.gravatar.com"), name="dispatch")
class SpeakerDetail(PermissionRequired, ActionFromUrl, CreateOrUpdateView):
    template_name = "orga/speaker/form.html"
    form_class = SpeakerProfileForm
    model = User
    permission_required = "orga.view_speaker"
    write_permission_required = "orga.change_speaker"

    def get_object(self):
        return get_object_or_404(
            User.objects.filter(
                submissions__in=Submission.all_objects.filter(event=self.request.event)
            )
            .order_by("id")
            .distinct(),
            pk=self.kwargs["pk"],
Esempio n. 11
0
    def next(self, request, event):
        from payments.models import CheckoutPayment
        order = get_order(request, event)

        payment = CheckoutPayment.from_order(order)
        payment.save()

        result = payment.perform_create_payment_request(request)

        return redirect(result["href"])


# Confirm view needs to be able to redirect to Checkout payment wall, so this needs to be included in CSP.
tickets_confirm_phase = ConfirmPhase()
tickets_confirm_view = csp_update(FORM_ACTION=CHECKOUT_PAYMENT_WALL_ORIGIN)(
    decorate(tickets_confirm_phase))


class ThanksPhase(Phase):
    name = "tickets_thanks_view"
    friendly_name = _("Thank you!")
    template = "tickets_thanks_phase.pug"
    prev_phase = None
    next_phase = "tickets_welcome_view"
    can_cancel = False

    def available(self, request, event):
        order = get_order(request, event)
        return order.is_confirmed and order.is_paid

    def vars(self, request, event, form):
Esempio n. 12
0
from django.contrib import admin
from django.contrib.flatpages import admin as flatpage_admin, forms as flatpage_forms
from django.contrib.flatpages.models import FlatPage
from django.utils import html
from django.utils.decorators import method_decorator
from django.utils.text import Truncator

from .models import HierarchicalFlatPage

# The CKEditorWidget requires inline css and javascript, which violate the
# site-wide CSP. This decorator loosens the CSP on the admin pages that load
# the editor widget.
_CSP_UPDATE_DECORATOR = csp_update(
    SCRIPT_SRC=("'unsafe-inline'", 'www.webspellchecker.net/spellcheck31/'),
    STYLE_SRC="'unsafe-inline'",
    IMG_SRC='data:',  # used by the iframe tools
    FRAME_SRC=(  # used by ckeditor spell checker
        'https://svc.webspellchecker.net/spellcheck/lf/23/banner/banner.html',
        'www.webspellchecker.net/spellcheck/script/ssrv.cgi'),
)


class ParentPageListFilter(admin.SimpleListFilter):
    title = "parent page"

    parameter_name = 'parent'

    def lookups(self, request, model_admin):
        pages = HierarchicalFlatPage.objects.children_for_url('/', depth=3)
        return [(p.url, p.url) for p in pages]

    def queryset(self, request, queryset):
Esempio n. 13
0
    Command as ExportScheduleHtml,
)
from pretalx.agenda.tasks import export_schedule_html
from pretalx.api.serializers.room import AvailabilitySerializer
from pretalx.common.mixins.views import (
    ActionFromUrl, EventPermissionRequired, PermissionRequired,
)
from pretalx.common.signals import register_data_exporters
from pretalx.common.views import CreateOrUpdateView
from pretalx.orga.forms.schedule import ScheduleImportForm, ScheduleReleaseForm
from pretalx.schedule.forms import QuickScheduleForm, RoomForm
from pretalx.schedule.models import Availability, Room
from pretalx.schedule.utils import guess_schedule_version


@method_decorator(csp_update(SCRIPT_SRC="'self' 'unsafe-eval'"), name='dispatch')
class ScheduleView(EventPermissionRequired, TemplateView):
    template_name = 'orga/schedule/index.html'
    permission_required = 'orga.view_schedule'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        version = self.request.GET.get('version')
        context['schedule_version'] = version
        context['active_schedule'] = (
            self.request.event.schedules.filter(version=version).first()
            if version
            else self.request.event.wip_schedule
        )
        return context
Esempio n. 14
0
                room.name,
                'talks': [
                    talk for talk in talks.filter(
                        start__gte=day_start(current_date),
                        start__lte=day_end(current_date),
                        room=room).order_by('start')
                ],
            } for room in event.rooms.all()],
        } for index, current_date in enumerate([
            event.datetime_from + timedelta(days=i)
            for i in range((event.date_to - event.date_from).days + 1)
        ])]
        return ctx


@method_decorator(csp_update(STYLE_SRC="'self' 'unsafe-inline'"),
                  name='dispatch')
class ScheduleView(ScheduleDataView):
    template_name = 'agenda/schedule.html'

    def get_object(self):
        obj = super().get_object()
        if not obj and self.request.is_orga:
            return self.request.event.wip_schedule
        return obj

    def get_context_data(self, *args, **kwargs):
        ctx = super().get_context_data(*args, **kwargs)
        tz = pytz.timezone(self.request.event.timezone)
        if 'data' in ctx:
            for date in ctx['data']:
Esempio n. 15
0
        form.instance.log_action(
            "pretalx_pages.page.added",
            data=dict(form.cleaned_data),
            person=self.request.user,
            orga=True,
        )
        return ret

    def form_invalid(self, form):
        messages.error(
            self.request, _("We could not save your changes. See below for details.")
        )
        return super().form_invalid(form)


@method_decorator(csp_update(IMG_SRC="*"), name="dispatch")
class ShowPageView(TemplateView):
    template_name = "pretalx_pages/show.html"

    def get_page(self):
        try:
            return Page.objects.get(
                event=self.request.event, slug__iexact=self.kwargs["slug"]
            )
        except Page.DoesNotExist:
            raise Http404(_("The requested page does not exist."))

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data()
        page = self.get_page()
        ctx["page_title"] = page.title
Esempio n. 16
0
    else:
        raise Http404()


@require_safe
@etag(lambda r: getattr(settings, "DONATE_BITCOIN_ADDRESS", ""))
@cache_page(60 * 60)  # NOTE: if only etag is set, Django doesn't include cache headers
def bitcoin_qrcode(request):
    """Return a QR Code for donating via Bitcoin."""
    address = getattr(settings, "DONATE_BITCOIN_ADDRESS", None)
    if address:
        raw = make_qr_code(
            bitcoin_donation_url(get_current_site(request).name, address)
        )
        return HttpResponse(raw.getvalue(), content_type="image/svg+xml")
    else:
        raise Http404


@method_decorator(
    csp_update(
        # https://github.com/praekelt/django-recaptcha/issues/101
        # https://developers.google.com/recaptcha/docs/faq#im-using-content-security-policy-csp-on-my-website.-how-can-i-configure-it-to-work-with-recaptcha
        SCRIPT_SRC="'self' 'unsafe-inline' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/",
        FRAME_SRC="'self' https://www.google.com/recaptcha/",
    ),
    name="dispatch",
)
class CaptchaSignupView(SignupView):
    pass
Esempio n. 17
0
    .. deprecated:: 0.8.2
       Removed in favor of :func:`~api.v2.views.rapidoc`.
    """
    return HttpResponse('Use /api/v2/docs/ instead', status=410)


def _rapidoc(request: HttpRequest) -> HttpResponse:
    """
    View that serves the RapiDoc_ documentation of the site.

    :param request: The original request.

    :return: A response with the rendered ``rapidoc.html`` template.

    .. _RapiDoc: https://mrin9.github.io/RapiDoc/
    """
    return render(request, 'rapidoc.html', {
        'schema': reverse('api:v2:schema'),
    })


if find_spec('csp'):  # pragma: no cover
    from csp.decorators import csp_update
    rapidoc = csp_update(style_src="'unsafe-inline'")(_rapidoc)
else:
    rapidoc = _rapidoc
rapidoc.__doc__ = _rapidoc.__doc__

__all__ = ['openapi', 'redoc_redirect', 'swagger_redirect', 'rapidoc']
Esempio n. 18
0
from django.conf.urls import url
from djangae.utils import djangae_webapp

from django.views.decorators.csrf import csrf_exempt

# The Mapreduce status UI uses inline JS, which will fail If we have django-csp
# installed and are not allowing 'unsafe-inline' as a SCRIPT_SRC.
try:
    from csp.decorators import csp_update
    exempt_from_unsafe_inline = csp_update(SCRIPT_SRC=("'unsafe-inline'", ))
except ImportError:
    exempt_from_unsafe_inline = lambda func: func

try:
    from mapreduce.main import create_handlers_map
    wrapped_urls = [
        url(url_re.replace('.*/', '^', 1),
            exempt_from_unsafe_inline(csrf_exempt(djangae_webapp(func))))
        for url_re, func in create_handlers_map()
    ]
except ImportError as e:
    wrapped_urls = []

urlpatterns = wrapped_urls
Esempio n. 19
0
"""The URLconf of the config app."""

from importlib.util import find_spec

from django.conf import settings
from django.contrib.flatpages.views import flatpage
from django.urls import include, path

info_page = flatpage
if find_spec('csp'):  # pragma: no cover
    from csp.decorators import csp_update
    info_page = csp_update(style_src="'unsafe-inline'",
                           img_src="https:")(flatpage)

#: The URL patterns of the config app.
urlpatterns = [
    path('info/', info_page, {'url': '/info/'}, name='info'),
    path('privacy/', info_page, {'url': '/privacy/'}, name='privacy'),
]

if settings.DEBUG:  # pragma: no cover
    from django.conf.urls.static import static
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)
    urlpatterns += static(settings.STATIC_URL,
                          document_root=settings.STATIC_ROOT)
    if find_spec('debug_toolbar'):
        from debug_toolbar import urls as djdt_urls
        urlpatterns.append(path('__debug__/', include(djdt_urls)))

__all__ = ['urlpatterns']
Esempio n. 20
0
    FormView,
    ListView,
    TemplateView,
    UpdateView,
    View,
)

from pretalx.cfp.forms.submissions import SubmissionInvitationForm
from pretalx.cfp.views.event import LoggedInEventPageMixin
from pretalx.common.phrases import phrases
from pretalx.person.forms import LoginInfoForm, SpeakerProfileForm
from pretalx.submission.forms import InfoForm, QuestionsForm, ResourceForm
from pretalx.submission.models import Resource, Submission, SubmissionStates


@method_decorator(csp_update(IMG_SRC="https://www.gravatar.com",
                             SCRIPT_SRC="'self' 'unsafe-inline'"),
                  name='dispatch')
class ProfileView(LoggedInEventPageMixin, TemplateView):
    template_name = 'cfp/event/user_profile.html'

    @cached_property
    def login_form(self):
        return LoginInfoForm(
            user=self.request.user,
            data=(self.request.POST if self.request.method == 'POST'
                  and self.request.POST.get('form') == 'login' else None))

    @cached_property
    def profile_form(self):
        if self.request.method == 'POST' and self.request.POST.get(
                'form') == 'profile':
Esempio n. 21
0
from csp.decorators import csp_update
from django.conf import settings
from django.conf.urls import url, include
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.views.decorators.http import etag

from nextcloudappstore.core.caching import app_rating_etag
from nextcloudappstore.core.feeds import AppReleaseAtomFeed, AppReleaseRssFeed
from nextcloudappstore.core.views import CategoryAppListView, AppDetailView, \
    app_description, AppReleasesView, AppUploadView, LegalNoticeView, \
    AppRatingApi, AppRegisterView, AppScaffoldingView

urlpatterns = [
    url(r'^$', CategoryAppListView.as_view(), {'id': None}, name='home'),
    url(r"^signup/$", csp_update(**settings.CSP_SIGNUP)(signup),
        name="account_signup"),
    url(r"^social/signup/$", csp_update(**settings.CSP_SIGNUP)(social_signup),
        name="socialaccount_signup"),
    url(r'^', include('allauth.urls')),
    url(r'^legal/?$', LegalNoticeView.as_view(), name='legal-notice'),
    url(r'^categories/(?P<id>[\w]*)/?$', CategoryAppListView.as_view(),
        name='category-app-list'),
    url(r'^developer/apps/generate/?$', AppScaffoldingView.as_view(),
        name='app-scaffold'),
    url(r'^developer/apps/releases/new/?$', AppUploadView.as_view(),
        name='app-upload'),
    url(r'^developer/apps/new/?$', AppRegisterView.as_view(),
        name='app-register'),
    url(r'^apps/(?P<id>[\w_]+)/?$', AppDetailView.as_view(),
        name='app-detail'),
Esempio n. 22
0
from django.views.generic import FormView, TemplateView, View
from i18nfield.utils import I18nJSONEncoder

from pretalx.agenda.management.commands.export_schedule_html import (
    Command as ExportScheduleHtml, )
from pretalx.agenda.tasks import export_schedule_html
from pretalx.common.mixins.views import ActionFromUrl, PermissionRequired
from pretalx.common.signals import register_data_exporters
from pretalx.common.views import CreateOrUpdateView
from pretalx.orga.forms.schedule import ScheduleImportForm, ScheduleReleaseForm
from pretalx.orga.views.event import EventSettingsPermission
from pretalx.schedule.forms import RoomForm
from pretalx.schedule.models import Availability, Room


@method_decorator(csp_update(SCRIPT_SRC="'self' 'unsafe-eval'"),
                  name='dispatch')
class ScheduleView(PermissionRequired, TemplateView):
    template_name = 'orga/schedule/index.html'
    permission_required = 'orga.view_schedule'

    def get_permission_object(self):
        return self.request.event

    def get_context_data(self, event):
        context = super().get_context_data()
        version = self.request.GET.get('version')
        context['schedule_version'] = version
        context['active_schedule'] = self.request.event.schedules.filter(
            version=version).first(
            ) if version else self.request.event.wip_schedule
Esempio n. 23
0
    else:
        raise Http404()


@require_safe
@etag(lambda r: getattr(settings, "DONATE_BITCOIN_ADDRESS", ""))
@cache_page(
    60 * 60)  # NOTE: if only etag is set, Django doesn't include cache headers
def bitcoin_qrcode(request):
    """Return a QR Code for donating via Bitcoin."""
    address = getattr(settings, "DONATE_BITCOIN_ADDRESS", None)
    if address:
        raw = make_qr_code(
            bitcoin_donation_url(get_current_site(request).name, address))
        return HttpResponse(raw.getvalue(), content_type="image/svg+xml")
    else:
        raise Http404


@method_decorator(
    csp_update(
        # https://github.com/praekelt/django-recaptcha/issues/101
        SCRIPT_SRC_ELEM=
        "'self' 'unsafe-inline' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/",
        FRAME_SRC="'self' https://www.google.com/recaptcha/",
    ),
    name="dispatch",
)
class CaptchaSignupView(SignupView):
    pass
Esempio n. 24
0
                qs = qs.filter(user__submissions__state__in=[
                    SubmissionStates.ACCEPTED,
                    SubmissionStates.CONFIRMED,
                ])
            elif self.request.GET['role'] == 'false':
                qs = qs.exclude(user__submissions__state__in=[
                    SubmissionStates.ACCEPTED,
                    SubmissionStates.CONFIRMED,
                ])

        qs = qs.order_by('id').distinct()
        qs = self.sort_queryset(qs)
        return qs


@method_decorator(csp_update(IMG_SRC="https://www.gravatar.com"),
                  name='dispatch')
class SpeakerDetail(PermissionRequired, ActionFromUrl, CreateOrUpdateView):
    template_name = 'orga/speaker/form.html'
    form_class = SpeakerProfileForm
    model = User
    permission_required = 'orga.view_speaker'
    write_permission_required = 'orga.change_speaker'

    def get_object(self):
        return get_object_or_404(
            User.objects.filter(
                Q(submissions__in=self.request.event.submissions.all())
                | Q(submissions__in=self.request.event.submissions(
                    manager='deleted_objects').all())).order_by(
                        'id').distinct(),
Esempio n. 25
0
        return qs


class DefaultPagination(LimitOffsetPagination):
    default_limit = 50
    max_limit = 100


@method_decorator(
    csp_update(
        IMG_SRC=(
            "maps.googleapis.com",  # Google Maps
            "maps.gstatic.com",  # Google Maps
            "cbks0.googleapis.com",
            "khms0.googleapis.com",
            "khms1.googleapis.com",
            "lh3.ggpht.com",
            "geo0.ggpht.com",  # Google Street View
            "geo1.ggpht.com",  # Google Street View
            "geo2.ggpht.com",  # Google Street View
            "geo3.ggpht.com",  # Google Street View
        ),
        SCRIPT_SRC=("maps.googleapis.com", "maps.gstatic.com")),
    name='dispatch')
class CallsignDetailView(DetailView):
    queryset = Callsign.objects\
        .select_related("prefix") \
        .select_related("prefix__dxcc") \
        .select_related("owner")\
        .select_related("country") \
        .select_related("country__telecommunicationagency") \
        .select_related("clubloguser") \