Пример #1
0
        def _wrapped_view(request, *args, **kwargs):
            LOGGER.debug("Enters djaoapp.decorators.requires_provider_only")
            site = get_current_site()
            organization = kwargs.get('organization', None)
            if site.db_name:
                # We have a separate database so it is OK for a manager
                # of the site to access registered ``Organization`` which
                # are not subscribed yet.
                if _has_valid_access(request, [get_current_broker()],
                                     strength):
                    return view_func(request, *args, **kwargs)
            try:
                app = get_current_app()
                #pylint:disable=unused-variable
                redirect_url, matched, session = check_matched(
                    request, app, prefixes=DEFAULT_PREFIXES)
                if redirect_url:
                    if isinstance(redirect_url, six.string_types):
                        return http.HttpResponseRedirect(redirect_url)
                    raise PermissionDenied()
            except NoRuleMatch:
                # By default, we are looking for provider.
                slug = kwargs.get('charge', organization)
                redirect_url = _fail_provider_only(
                    request,
                    organization=slug,
                    roledescription=roledescription,
                    strength=strength)
                if redirect_url:
                    return redirect_or_denied(request, redirect_url,
                        redirect_field_name=redirect_field_name,
                        descr=_("%(auth)s is not a manager of one of"\
" %(organization)s providers.") % {'auth': request.user, 'organization': slug})
            return view_func(request, *args, **kwargs)
Пример #2
0
        def _wrapped_view(request, *args, **kwargs):
            LOGGER.debug("Enters djaoapp.decorators.requires_self_provider")
            site = get_current_site()
            if site.db_name:
                # We have a separate database so it is OK for a manager
                # of the site to access profiles of ``User`` which
                # are not subscribed yet.
                if _has_valid_access(request, [get_current_broker()],
                                     strength):
                    return view_func(request, *args, **kwargs)
            try:
                app = get_current_app()
                #pylint:disable=unused-variable
                redirect_url, matched, session = check_matched(
                    request, app, prefixes=DEFAULT_PREFIXES)
                if redirect_url:
                    if isinstance(redirect_url, six.string_types):
                        return http.HttpResponseRedirect(redirect_url)
                    raise PermissionDenied()
            except NoRuleMatch:
                redirect_url = _fail_self_provider(request,
                                                   user=kwargs.get(
                                                       'user', None),
                                                   strength=strength)
                if redirect_url:
                    return redirect_or_denied(request, redirect_url,
                        redirect_field_name=redirect_field_name,
                        descr=_("%(auth)s has neither a direct"\
" relation to an organization connected to %(user)s nor a connection to one"\
" of the providers to such organization.") % {
                    'auth': request.user, 'user': kwargs.get('user', None)})
            return view_func(request, *args, **kwargs)
Пример #3
0
        def _wrapped_view(request, *args, **kwargs):
            try:
                app = get_current_app()
                redirect_url, _, _ = check_matched(
                    request,
                    app,
                    prefixes=[
                        '/api/billing/', '/api/metrics/', '/api/profile/',
                        '/api/users/', '/billing/', '/metrics/', '/profile/',
                        '/users/'
                    ])
                if redirect_url:
                    if isinstance(redirect_url, six.string_types):
                        return http.HttpResponseRedirect(redirect_url)
                    raise PermissionDenied()
            except NoRuleMatch:
                slug = kwargs.get('charge', kwargs.get('organization', None))
                redirect_url = _fail_direct(request,
                                            organization=slug,
                                            roledescription=roledescription,
                                            strength=strength)
                if redirect_url:
                    return redirect_or_denied(
                        request,
                        redirect_url,
                        redirect_field_name=redirect_field_name,
                        descr="%(user)s is not a direct manager '\
    ' of %(organization)s." % {
                            'user': request.user,
                            'organization': slug
                        })

            return view_func(request, *args, **kwargs)
Пример #4
0
 def get(self, request, *args, **kwargs):
     #pylint:disable=unused-argument,no-self-use
     context = {}
     app = get_current_app()
     try:
         storage = get_default_storage(request, account=app.account)
         # The following statement will raise an Exception
         # when we are dealing with a ``FileSystemStorage``.
         location = "s3://%s/%s" % (storage.bucket_name, storage.location)
         aws_region = context.get('aws_region', None)
         context.update(aws_bucket_context(request, location,
             aws_upload_role=app.role_name,
             aws_external_id=app.external_id,
             aws_region=aws_region,
             acls=['private', 'public-read']))
         if request.query_params.get('public', False):
             context.update({
                 'policy': context['public_read_aws_policy'],
                 'signature': context['public_read_aws_policy_signature'],
             })
         else:
             context.update({
                 'policy': context['private_aws_policy'],
                 'signature': context['private_aws_policy_signature'],
             })
     except AttributeError:
         LOGGER.debug("doesn't look like we have a S3Storage.")
     serializer = self.get_serializer(data=context)
     serializer.is_valid(raise_exception=True)
     return Response(serializer.validated_data)
Пример #5
0
def fail_self_provider(request, user=None, roledescription=None):
    """
    Same decorator as saas.requires_self_provider with the added permissions
    that managers of the site database itself are also able to access
    profiles of registered yet unsubscribed ``Organization``.
    """
    site = get_current_site()
    if site.db_name and site.db_name != DEFAULT_DB_ALIAS:
        # We have a separate database so it is OK for a manager
        # of the site to access registered ``Organization`` which
        # are not subscribed yet.
        if _has_valid_access(request, [get_current_broker()]):
            return False
    try:
        app = get_current_app()
        #pylint:disable=unused-variable
        redirect, matched, session = check_matched(request,
                                                   app,
                                                   prefixes=DEFAULT_PREFIXES)
    except NoRuleMatch:
        # By default, we are looking for provider.
        redirect = fail_self_provider_default(request,
                                              user=user,
                                              roledescription=roledescription)
    return redirect
Пример #6
0
        def _wrapped_view(request, *args, **kwargs):
            site = get_current_site()
            if site.db_name:
                # We have a separate database so it is OK for a manager
                # of the site to access profiles of ``User`` which
                # are not subscribed yet.
                if _has_valid_access(request, [get_current_broker()],
                                     strength):
                    return view_func(request, *args, **kwargs)
            try:
                app = get_current_app()
                redirect_url, _, _ = check_matched(
                    request,
                    app,
                    prefixes=[
                        '/api/billing/', '/api/metrics/', '/api/profile/',
                        '/api/users/', '/billing/', '/metrics/', '/profile/',
                        '/users/'
                    ])
                if redirect_url:
                    if isinstance(redirect_url, six.string_types):
                        return http.HttpResponseRedirect(redirect_url)
                    raise PermissionDenied()
            except NoRuleMatch:
                if _fail_self_provider(request,
                                       user=kwargs.get('user', None),
                                       strength=strength):
                    raise PermissionDenied("%(auth)s has neither a direct"\
" relation to an organization connected to %(user)s nor a connection to one"\
"of the providers to such organization." % {
                    'auth': request.user, 'user': kwargs.get('user', None)})
            return view_func(request, *args, **kwargs)
Пример #7
0
 def get_context_data(self, **kwargs):
     context = super(DjaoAppMixin, self).get_context_data(**kwargs)
     context.update({'edit_perm':
                     self.edit_perm})  # XXX generic_navbar.html
     if self.organization:
         if not Plan.objects.filter(
                 organization=self.organization).exists():
             context.update({'next_url': reverse('saas_cart_plan_list')})
     # URLs for user
     if self.request.user.is_authenticated():
         urls = {
             'user': {
                 'logout':
                 reverse('logout'),
                 'profile':
                 reverse('users_profile', args=(self.request.user, )),
             }
         }
     else:
         urls = {
             'user': {
                 'login':
                 reverse('login'),
                 'login_github':
                 reverse('social:begin', args=('github', )),
                 'login_google':
                 reverse('social:begin', args=('google-oauth2', )),
                 'login_twitter':
                 reverse('social:begin', args=('twitter', )),
                 'password_reset':
                 reverse('password_reset'),
                 'register':
                 reverse('registration_register'),
             }
         }
     # URLs for provider
     app = get_current_app()
     # ``app.account`` is guarenteed to be in the same database as ``app``.
     # ``site.account`` is always in the *default* database, which is not
     # the expected database ``Organization`` are typically queried from.
     provider = app.account
     if not fail_direct(self.request, organization=provider):
         urls.update({
             'provider': {
                 'dashboard': reverse('saas_dashboard', args=(provider, )),
             }
         })
     if 'urls' in context:
         for key, val in six.iteritems(urls):
             if key in context['urls']:
                 context['urls'][key].update(val)
             else:
                 context['urls'].update({key: val})
     else:
         context.update({'urls': urls})
     return context
Пример #8
0
def fail_direct(request, organization=None, roledescription=None):
    try:
        app = get_current_app()
        #pylint:disable=unused-variable
        redirect, matched, session = check_matched(request,
                                                   app,
                                                   prefixes=DEFAULT_PREFIXES)
    except NoRuleMatch:
        redirect = fail_direct_default(request,
                                       organization=organization,
                                       roledescription=roledescription)
    return redirect
Пример #9
0
def fail_edit_perm(request, account=None):
    """
    Returns ``True`` if the request user does not have edit permissions.
    """
    result = True
    # The context processor will be called from the e-mail sender
    # which might not be associated to a request.
    if request is not None:
        if account is None:
            account = get_current_app().account
        result = not bool(_valid_manager(request, [account]))
    return result
Пример #10
0
 def get_context_data(self, **kwargs):
     context = super(PricingView, self).get_context_data(**kwargs)
     if self.edit_perm:
         app = get_current_app()
         if app.show_edit_tools:
             context.update({
                 'show_show_edit_tools': app.show_edit_tools,
                 'plan': Plan()
             })
             if not self.object_list.exists():
                 messages.info(self.request, _("No Plans yet."\
                     " Click the 'Add Plan' button to create one."))
     return context
Пример #11
0
def djaoapp_urls(request, account=None, base=None):
    if account is None:
        account = get_current_app().account
    urls = {
        'pricing':
        build_absolute_uri(request,
                           location='/pricing/',
                           site=settings.APP_NAME),
        'cart':
        build_absolute_uri(request,
                           location='/billing/%s/cart/' % account,
                           site=settings.APP_NAME)
    }
    if base:
        urls.update({
            'app':
            build_absolute_uri(request,
                               location='/app/%s/' % base,
                               site="%s-master" % settings.DB_NAME)
        })  # XXX Hack for correct domain
    return urls
Пример #12
0
def fail_authenticated(request, verification_key=None):
    """
    Decorator for views that checks that the user is authenticated.

    ``django.contrib.auth.decorators.login_required`` will automatically
    redirect to the login page. We wante to redirect to the activation
    page when required, as well as raise a ``PermissionDenied``
    instead when Content-Type is showing we are dealing with an API request.
    """
    try:
        app = get_current_app()
        #pylint:disable=unused-variable
        redirect, matched, session = check_matched(request,
                                                   app,
                                                   prefixes=DEFAULT_PREFIXES)
    except NoRuleMatch:
        redirect = fail_authenticated_default(request)
        if redirect:
            if verification_key:
                contact = Contact.objects.filter(
                    Q(email_verification_key=verification_key)
                    | Q(phone_verification_key=verification_key)).first()
                if not contact:
                    # Not a `Contact`, let's try `Role`.
                    role_model = get_role_model()
                    try:
                        role = role_model.objects.filter(
                            Q(grant_key=verification_key)
                            | Q(request_key=verification_key)).get()
                        contact, _ = Contact.objects.prepare_email_verification(
                            role.user, role.user.email)
                        verification_key = contact.email_verification_key
                    except role_model.DoesNotExist:
                        pass
                if contact and has_invalid_password(contact.user):
                    redirect = request.build_absolute_uri(
                        reverse('registration_activate',
                                args=(verification_key, )))
    return redirect
Пример #13
0
        def _wrapped_view(request, *args, **kwargs):
            LOGGER.debug("Enters djaoapp.decorators.requires_direct")
            try:
                app = get_current_app()
                #pylint:disable=unused-variable
                redirect_url, matched, session = check_matched(request, app,
                    prefixes=DEFAULT_PREFIXES)
                if redirect_url:
                    if isinstance(redirect_url, six.string_types):
                        return http.HttpResponseRedirect(redirect_url)
                    raise PermissionDenied()
            except NoRuleMatch:
                slug = kwargs.get('charge', kwargs.get('organization', None))
                redirect_url = _fail_direct(request, organization=slug,
                        roledescription=roledescription, strength=strength)
                if redirect_url:
                    return redirect_or_denied(request, redirect_url,
                        redirect_field_name=redirect_field_name,
                        descr=_("%(auth)s is not a direct manager"\
    " of %(organization)s.") % {'auth': request.user, 'organization': slug})

            return view_func(request, *args, **kwargs)
Пример #14
0
    def get(self, request, *args, **kwargs):
        context = {}
        app = get_current_app()
        try:
            # The following statement will raise an Exception
            # when we are dealing with a ``FileSystemStorage``.
            _ = get_storage_class().bucket_name
            bucket_name = get_bucket_name(app)
            media_prefix = get_media_prefix(app)
            aws_region = context.get('aws_region', None)
            context.update(
                aws_bucket_context(request,
                                   bucket_name,
                                   aws_upload_role=app.role_name,
                                   aws_external_id=app.external_id,
                                   aws_region=aws_region,
                                   acls=['private', 'public-read']))
            if request.query_params.get('public', False):
                context.update({
                    'policy':
                    context['public_read_aws_policy'],
                    'signature':
                    context['public_read_aws_policy_signature'],
                })
            else:
                context.update({
                    'policy':
                    context['private_aws_policy'],
                    'signature':
                    context['private_aws_policy_signature'],
                })
            context.update({'media_prefix': media_prefix})
        except AttributeError:
            LOGGER.debug("doesn't look like we have a S3Storage.")

        serializer = CredentialsSerializer(data=context)
        serializer.is_valid(raise_exception=True)
        return Response(serializer.validated_data)
Пример #15
0
        def _wrapped_view(request, *args, **kwargs):
            site = get_current_site()
            organization = kwargs.get('organization', None)
            if site.db_name:
                # We have a separate database so it is OK for a manager
                # of the site to access registered ``Organization`` which
                # are not subscribed yet.
                if _has_valid_access(request, [get_current_broker()],
                                     strength):
                    return view_func(request, *args, **kwargs)
            try:
                app = get_current_app()
                redirect_url, _, _ = check_matched(
                    request,
                    app,
                    prefixes=[
                        '/api/billing/', '/api/metrics/', '/api/profile/',
                        '/api/users/', '/billing/', '/metrics/', '/profile/',
                        '/users/'
                    ])
                if redirect_url:
                    if isinstance(redirect_url, six.string_types):
                        return http.HttpResponseRedirect(redirect_url)
                    raise PermissionDenied()
            except NoRuleMatch:
                # By default, we are looking for provider.
                slug = kwargs.get('charge', organization)
                redirect_url = _fail_provider_only(
                    request,
                    organization=slug,
                    roledescription=roledescription,
                    strength=strength)
                if redirect_url:
                    return redirect_or_denied(request, redirect_url,
                        "%(user)s is not a manager of one of"\
" %(slug)s providers." % {'user': request.user, 'slug': slug})
            return view_func(request, *args, **kwargs)
Пример #16
0
def inject_edition_tools(response,
                         request,
                         context=None,
                         body_top_template_name=None,
                         body_bottom_template_name=None):
    """
    If the ``request.user`` has editable permissions, this method
    injects the edition tools into the html *content* and return
    a BeautifulSoup object of the resulting content + tools.

    If the response is editable according to the proxy rules, this
    method returns a BeautifulSoup object of the content such that
    ``PageMixin`` inserts the edited page elements.
    """
    #pylint:disable=too-many-locals
    if context is None:
        context = {}
    dj_urls = {}
    edit_urls = {}
    provider_urls = {}
    site = get_current_site()
    app = get_current_app()
    # ``app.account`` is guarenteed to be in the same database as ``app``.
    # ``site.account`` is always in the *default* database, which is not
    # the expected database ``Organization`` are typically queried from.
    provider = app.account
    enable_code_editor = False
    edit_urls = {
        'api_medias': reverse('uploaded_media_elements', kwargs={'path': ''}),
        'api_sitecss': reverse('edit_sitecss'),
        'api_less_overrides': reverse('pages_api_less_overrides'),
        'api_sources': reverse('pages_api_sources'),
        'api_page_elements': reverse('page_elements'),
        'api_plans': reverse('saas_api_plans', args=(provider, )),
        'plan_update_base': reverse('saas_plan_base', args=(provider, ))
    }

    if not fail_edit_perm(request, account=provider):
        body_bottom_template_name = "pages/_body_bottom.html"
        try:
            # The following statement will raise an Exception
            # when we are dealing with a ``FileSystemStorage``.
            _ = get_storage_class().bucket_name
            edit_urls.update({'media_upload': reverse('api_credentials')})
        except AttributeError:
            LOGGER.debug("doesn't look like we have a S3Storage.")

        enable_code_editor = is_streetside(site)
        if not has_bank_account(provider):
            provider_urls = {
                'bank': reverse('saas_update_bank', args=(provider, ))
            }
            body_top_template_name = "pages/_body_top_connect_processor.html"
        elif is_testing(site):
            if enable_code_editor:
                dj_urls = djaoapp_urls(request,
                                       account=provider,
                                       base=site.as_base())
                body_top_template_name = "pages/_body_top_streetside_cart.html"
            else:
                dj_urls = djaoapp_urls(request, account=provider)
                body_top_template_name = "pages/_body_top_mallspace_cart.html"
        # XXX ``is_streetside(site)`` shouldn't disable all edit functionality.
        # Just the edition of templates.
        body_bottom_template_name = "pages/_body_bottom_edit_tools.html"
    elif not has_bank_account(provider) or is_testing(site):
        body_top_template_name = "pages/_body_top.html"
    if not (body_top_template_name or body_bottom_template_name):
        return None
    context.update({
        'ENABLE_CODE_EDITOR': enable_code_editor,
        'FEATURE_DEBUG': settings.FEATURES_DEBUG,
        'urls': {
            'provider': provider_urls,
            'djaodjin': dj_urls,
            'edit': edit_urls
        }
    })
    context.update(csrf(request))
    return pages_inject_edition_tools(
        response,
        request,
        context=context,
        body_top_template_name=body_top_template_name,
        body_bottom_template_name=body_bottom_template_name)
Пример #17
0
def inject_edition_tools(response,
                         request,
                         context=None,
                         body_top_template_name=None,
                         body_bottom_template_name=None):
    """
    If the ``request.user`` has editable permissions, this method
    injects the edition tools into the html *content* and return
    a BeautifulSoup object of the resulting content + tools.

    If the response is editable according to the proxy rules, this
    method returns a BeautifulSoup object of the content such that
    ``PageMixin`` inserts the edited page elements.
    """
    #pylint:disable=too-many-locals,too-many-nested-blocks,too-many-statements
    content_type = response.get('content-type', '')
    if not content_type.startswith('text/html'):
        return None

    if not is_authenticated(request):
        return None

    if context is None:
        context = {}

    # ``app.account`` is guarenteed to be in the same database as ``app``.
    # ``site.account`` is always in the *default* database, which is not
    # the expected database ``Organization`` are typically queried from.
    app = get_current_app()
    provider = app.account
    soup = None
    if app.show_edit_tools and get_role_model().objects.valid_for(
            organization=provider, user=request.user):
        edit_urls = {
            'api_medias':
            reverse('uploaded_media_elements', kwargs={'path': ''}),
            'api_sitecss':
            reverse('edit_sitecss'),
            'api_less_overrides':
            reverse('pages_api_less_overrides'),
            'api_sources':
            reverse('pages_api_sources'),
            'api_page_element_base':
            reverse('api_page_element', kwargs={'path': ''}),
            'api_plans':
            reverse('saas_api_plans', args=(provider, )),
            'plan_update_base':
            reverse('saas_plan_base', args=(provider, ))
        }
        try:
            # The following statement will raise an Exception
            # when we are dealing with a ``FileSystemStorage``.
            _ = get_storage_class().bucket_name
            edit_urls.update(
                {'media_upload': reverse('api_credentials_organization')})
        except AttributeError:
            LOGGER.debug("doesn't look like we have a S3Storage.")
        # XXX sites which are hosted on a same domain shouldn't disable
        # all edit functionality, just the edition of base templates.
        site = get_current_site()
        enable_code_editor = is_domain_site(site)
        if enable_code_editor:
            dj_urls = djaoapp_urls(request,
                                   account=provider,
                                   base=site.as_base())
            body_bottom_template_name = "pages/_body_bottom_edit_tools.html"
        else:
            dj_urls = djaoapp_urls(request, account=provider)
            body_bottom_template_name = "pages/_body_bottom.html"

        context.update({
            'ENABLE_CODE_EDITOR': enable_code_editor,
            'FEATURE_DEBUG': settings.FEATURES_DEBUG,
            'urls': {
                'djaodjin': dj_urls,
                'edit': edit_urls
            }
        })
        context.update(csrf(request))
        soup = pages_inject_edition_tools(
            response,
            request,
            context=context,
            body_top_template_name=body_top_template_name,
            body_bottom_template_name=body_bottom_template_name)

    # Insert the authenticated user information and roles on organization.
    if not soup:
        soup = BeautifulSoup(response.content, 'html5lib')
    if soup and soup.body:
        # Implementation Note: we have to use ``.body.next`` here
        # because html5lib "fixes" our HTML by adding missing
        # html/body tags. Furthermore if we use
        #``soup.body.insert(1, BeautifulSoup(body_top, 'html.parser'))``
        # instead, later on ``soup.find_all(class_=...)`` returns
        # an empty set though ``soup.prettify()`` outputs the full
        # expected HTML text.
        auth_user = soup.body.find(class_='header-menubar')
        user_menu_template = '_menubar.html'
        if auth_user and user_menu_template:
            serializer_class = import_string(rules_settings.SESSION_SERIALIZER)
            serializer = serializer_class(request)
            path_parts = reversed(request.path.split('/'))
            top_accessibles = []
            has_broker_role = False
            active_organization = None
            for role, organizations in six.iteritems(serializer.data['roles']):
                for organization in organizations:
                    if organization['slug'] == request.user.username:
                        # Personal Organization
                        continue
                    db_obj = get_organization_model().objects.get(
                        slug=organization['slug'])  # XXX Remove query.
                    if db_obj.is_provider:
                        settings_location = reverse(
                            'saas_dashboard', args=(organization['slug'], ))
                    else:
                        settings_location = reverse(
                            'saas_organization_profile',
                            args=(organization['slug'], ))
                    app_location = reverse('organization_app',
                                           args=(organization['slug'], ))
                    if organization['slug'] in path_parts:
                        active_organization = TopAccessibleOrganization(
                            organization['slug'],
                            organization['printable_name'], settings_location,
                            role, app_location)
                    if is_broker(organization['slug']):
                        has_broker_role = True
                    top_accessibles += [
                        TopAccessibleOrganization(
                            organization['slug'],
                            organization['printable_name'], settings_location,
                            role, app_location)
                    ]
            if not active_organization and has_broker_role:
                active_organization = get_broker()
            context.update({'active_organization': active_organization})
            context.update({'top_accessibles': top_accessibles})
            template = loader.get_template(user_menu_template)
            user_menu = render_template(template, context, request).strip()
            auth_user.clear()
            els = BeautifulSoup(user_menu, 'html5lib').body.ul.children
            for elem in els:
                auth_user.append(BeautifulSoup(str(elem), 'html5lib'))

    return soup
Пример #18
0
    def get_context_data(self, **kwargs):
        context = super(ExtraMixin, self).get_context_data(**kwargs)

        # Flags used in saas/_body_top_template.html to show how processor
        # handles cards and charges.
        #   if broker not configured
        #      return configure_broker()
        #   if broker in testmode
        #      return configure_broker_livemode()
        #   if provider not connected
        #      return connect_provider()
        #   if provider in testmode
        #      return connect_provider_livemode()
        app = get_current_app()
        # ``app.account`` is guarenteed to be in the same database as ``app``.
        # ``site.account`` is always in the *default* database, which is not
        # the expected database ``Organization`` are typically queried from.

        # XXX Relying on settings instead of saas_settings to cut down
        # on imports. `requires_provider_keys` matches definition
        # in `saas.backends.stripe_processor.base.StripeBackend`.
        broker_pub_key = getattr(settings, 'STRIPE_PUB_KEY', None)
        processor_backend_mode = settings.SAAS.get('PROCESSOR',
                                                   STRIPE_CONNECT_LOCAL).get(
                                                       'MODE',
                                                       STRIPE_CONNECT_LOCAL)
        requires_provider_keys = (processor_backend_mode
                                  in (STRIPE_CONNECT_FORWARD,
                                      STRIPE_CONNECT_REMOTE)
                                  and not self._is_platform(app.account))
        provider_pub_key = app.account.processor_pub_key

        processor_hint = None
        if not broker_pub_key:
            processor_hint = 'configure_broker'
        elif requires_provider_keys and not provider_pub_key:
            processor_hint = 'connect_provider'
        elif not broker_pub_key.startswith('pk_live_'):
            processor_hint = 'configure_broker_livemode'
        elif (requires_provider_keys
              and not provider_pub_key.startswith('pk_live_')):
            processor_hint = 'connect_provider_livemode'
        context.update({'processor_hint': processor_hint})
        self.update_context_urls(
            context, {
                'provider': {
                    'bank': reverse('saas_update_bank', args=(app.account, ))
                }
            })

        if not is_broker(self.organization):
            if 'urls' in context:
                if 'pages' in context['urls']:
                    del context['urls']['pages']
                if 'rules' in context['urls']:
                    del context['urls']['rules']
        # XXX might be overkill to always add ``site`` even though
        # it is only used in ``templates/saas/users/roles.html`` at this point.
        context.update({'site': get_current_site()})
        self.update_context_urls(
            context, {
                'profile_redirect': reverse('accounts_profile'),
            })
        # `ExtraMixin.get_context_data` is called before
        # `OrganizationMixin.get_context_data` had an opportunity to
        # add the organization to the `context`, so we call
        # `OrganizationMixin.organization` here. hmmm.
        attached_user = self.organization.attached_user()
        if attached_user:
            self.update_context_urls(
                context,
                {
                    'user': {
                        # The following are copy/pasted
                        # from `signup.UserProfileView`
                        # to be used in the personal profile page.
                        'api_generate_keys':
                        reverse('api_generate_keys', args=(attached_user, )),
                        'api_profile':
                        reverse('api_user_profile', args=(attached_user, )),
                        'api_password_change':
                        reverse('api_user_password_change',
                                args=(attached_user, )),
                        'api_contact':
                        reverse('api_contact',
                                args=(attached_user.username, )),  #XXX
                        'api_pubkey':
                        reverse('api_pubkey', args=(attached_user, )),
                        'password_change':
                        reverse('password_change', args=(attached_user, )),
                        # For sidebar menu items on personal profiles.
                        'accessibles':
                        reverse('saas_user_product_list',
                                args=(attached_user, )),
                        'notifications':
                        reverse('users_notifications', args=(attached_user, )),
                        'profile':
                        reverse('users_profile', args=(attached_user, )),
                    }
                })
            # XXX we can't enable this code until we remove references
            # to `User = get_user_model()` in signup.compat.
            #if has_valid_password(attached_user):
            #    self.update_context_urls(context, {'user': {
            #        'api_activate': reverse(
            #            'api_user_activate', args=(attached_user,)),
            #    }})

        # XXX temporarily until djaodjin-saas v0.5 is released.
        #if not is_broker(self.organization):
        #    self.update_context_urls(context, {
        #        'rules': {'app': None},
        #        'pages': {'theme_base': None},
        #    })
        return context