示例#1
0
def configure(settings=None):
    if settings is None:
        settings = {}

    # Add information about the current copy of the code.
    settings.setdefault("warehouse.commit", __commit__)

    # Set the environment from an environment variable, if one hasn't already
    # been set.
    maybe_set(
        settings,
        "warehouse.env",
        "WAREHOUSE_ENV",
        Environment,
        default=Environment.production,
    )

    # Pull in default configuration from the environment.
    maybe_set(settings, "warehouse.token", "WAREHOUSE_TOKEN")
    maybe_set(settings, "warehouse.num_proxies", "WAREHOUSE_NUM_PROXIES", int)
    maybe_set(settings, "warehouse.theme", "WAREHOUSE_THEME")
    maybe_set(settings, "warehouse.domain", "WAREHOUSE_DOMAIN")
    maybe_set(settings, "forklift.domain", "FORKLIFT_DOMAIN")
    maybe_set(settings, "warehouse.legacy_domain", "WAREHOUSE_LEGACY_DOMAIN")
    maybe_set(settings, "site.name", "SITE_NAME", default="Warehouse")
    maybe_set(settings, "aws.key_id", "AWS_ACCESS_KEY_ID")
    maybe_set(settings, "aws.secret_key", "AWS_SECRET_ACCESS_KEY")
    maybe_set(settings, "aws.region", "AWS_REGION")
    maybe_set(settings, "gcloud.credentials", "GCLOUD_CREDENTIALS")
    maybe_set(settings, "gcloud.project", "GCLOUD_PROJECT")
    maybe_set(settings, "warehouse.trending_table", "WAREHOUSE_TRENDING_TABLE")
    maybe_set(settings, "celery.broker_url", "AMQP_URL")
    maybe_set(settings, "celery.result_url", "REDIS_URL")
    maybe_set(settings, "celery.scheduler_url", "REDIS_URL")
    maybe_set(settings, "database.url", "DATABASE_URL")
    maybe_set(settings, "elasticsearch.url", "ELASTICSEARCH_URL")
    maybe_set(settings, "sentry.dsn", "SENTRY_DSN")
    maybe_set(settings, "sentry.transport", "SENTRY_TRANSPORT")
    maybe_set(settings, "sessions.url", "REDIS_URL")
    maybe_set(settings, "download_stats.url", "REDIS_URL")
    maybe_set(settings, "ratelimit.url", "REDIS_URL")
    maybe_set(settings, "recaptcha.site_key", "RECAPTCHA_SITE_KEY")
    maybe_set(settings, "recaptcha.secret_key", "RECAPTCHA_SECRET_KEY")
    maybe_set(settings, "sessions.secret", "SESSION_SECRET")
    maybe_set(settings, "camo.url", "CAMO_URL")
    maybe_set(settings, "camo.key", "CAMO_KEY")
    maybe_set(settings, "docs.url", "DOCS_URL")
    maybe_set(settings, "mail.host", "MAIL_HOST")
    maybe_set(settings, "mail.port", "MAIL_PORT")
    maybe_set(settings, "mail.username", "MAIL_USERNAME")
    maybe_set(settings, "mail.password", "MAIL_PASSWORD")
    maybe_set(settings, "mail.sender", "MAIL_SENDER")
    maybe_set(settings, "mail.ssl", "MAIL_SSL", default=True)
    maybe_set(settings, "ga.tracking_id", "GA_TRACKING_ID")
    maybe_set(settings, "statuspage.url", "STATUSPAGE_URL")
    maybe_set(settings, "token.password.secret", "TOKEN_PASSWORD_SECRET")
    maybe_set(settings, "token.email.secret", "TOKEN_EMAIL_SECRET")
    maybe_set(
        settings,
        "token.password.max_age",
        "TOKEN_PASSWORD_MAX_AGE",
        coercer=int,
    )
    maybe_set(
        settings,
        "token.email.max_age",
        "TOKEN_EMAIL_MAX_AGE",
        coercer=int,
    )
    maybe_set(
        settings,
        "token.default.max_age",
        "TOKEN_DEFAULT_MAX_AGE",
        coercer=int,
        default=21600,  # 6 hours
    )
    maybe_set_compound(settings, "files", "backend", "FILES_BACKEND")
    maybe_set_compound(settings, "origin_cache", "backend", "ORIGIN_CACHE")

    # Add the settings we use when the environment is set to development.
    if settings["warehouse.env"] == Environment.development:
        settings.setdefault("enforce_https", False)
        settings.setdefault("pyramid.reload_assets", True)
        settings.setdefault("pyramid.reload_templates", True)
        settings.setdefault("pyramid.prevent_http_cache", True)
        settings.setdefault("debugtoolbar.hosts", ["0.0.0.0/0"])
        settings.setdefault(
            "debugtoolbar.panels",
            [
                ".".join(["pyramid_debugtoolbar.panels", panel]) for panel in [
                    "versions.VersionDebugPanel",
                    "settings.SettingsDebugPanel",
                    "headers.HeaderDebugPanel",
                    "request_vars.RequestVarsDebugPanel",
                    "renderings.RenderingsDebugPanel",
                    "logger.LoggingPanel",
                    "performance.PerformanceDebugPanel",
                    "routes.RoutesDebugPanel",
                    "sqla.SQLADebugPanel",
                    "tweens.TweensDebugPanel",
                    "introspection.IntrospectionDebugPanel",
                ]
            ],
        )

    # Actually setup our Pyramid Configurator with the values pulled in from
    # the environment as well as the ones passed in to the configure function.
    config = Configurator(settings=settings)
    config.set_root_factory(RootFactory)

    # Register our CSRF support. We do this here, immediately after we've
    # created the Configurator instance so that we ensure to get our defaults
    # set ASAP before anything else has a chance to set them and possibly call
    # Configurator().commit()
    config.include(".csrf")

    # Include anything needed by the development environment.
    if config.registry.settings["warehouse.env"] == Environment.development:
        config.include("pyramid_debugtoolbar")

    # Register our logging support
    config.include(".logging")

    # We'll want to use Jinja2 as our template system.
    config.include("pyramid_jinja2")

    # Including pyramid_mailer for sending emails through SMTP.
    config.include("pyramid_mailer")

    # We want to use newstyle gettext
    config.add_settings({"jinja2.newstyle": True})

    # We also want to use Jinja2 for .html templates as well, because we just
    # assume that all templates will be using Jinja.
    config.add_jinja2_renderer(".html")

    # Sometimes our files are .txt files and we still want to use Jinja2 to
    # render them.
    config.add_jinja2_renderer(".txt")

    # Anytime we want to render a .xml template, we'll also use Jinja.
    config.add_jinja2_renderer(".xml")

    # We need to enable our Client Side Include extension
    config.get_settings().setdefault(
        "jinja2.extensions",
        ["warehouse.utils.html.ClientSideIncludeExtension"],
    )

    # We'll want to configure some filters for Jinja2 as well.
    filters = config.get_settings().setdefault("jinja2.filters", {})
    filters.setdefault(
        "format_classifiers",
        "warehouse.filters:format_classifiers",
    )
    filters.setdefault("format_tags", "warehouse.filters:format_tags")
    filters.setdefault("json", "warehouse.filters:tojson")
    filters.setdefault("readme", "warehouse.filters:readme")
    filters.setdefault("shorten_number", "warehouse.filters:shorten_number")
    filters.setdefault("urlparse", "warehouse.filters:urlparse")
    filters.setdefault("contains_valid_uris",
                       "warehouse.filters:contains_valid_uris")
    filters.setdefault("format_package_type",
                       "warehouse.filters:format_package_type")
    filters.setdefault("parse_version", "warehouse.filters:parse_version")

    # We also want to register some global functions for Jinja
    jglobals = config.get_settings().setdefault("jinja2.globals", {})
    jglobals.setdefault("is_valid_uri", "warehouse.utils.http:is_valid_uri")
    jglobals.setdefault("gravatar", "warehouse.utils.gravatar:gravatar")
    jglobals.setdefault("gravatar_profile", "warehouse.utils.gravatar:profile")
    jglobals.setdefault("now", "warehouse.utils:now")

    # We'll store all of our templates in one location, warehouse/templates
    # so we'll go ahead and add that to the Jinja2 search path.
    config.add_jinja2_search_path("warehouse:templates", name=".html")
    config.add_jinja2_search_path("warehouse:templates", name=".txt")
    config.add_jinja2_search_path("warehouse:templates", name=".xml")

    # We want to configure our JSON renderer to sort the keys, and also to use
    # an ultra compact serialization format.
    config.add_renderer(
        "json",
        renderers.JSON(sort_keys=True, separators=(",", ":")),
    )

    # Configure retry support.
    config.add_settings({"retry.attempts": 3})
    config.include("pyramid_retry")

    # Configure our transaction handling so that each request gets its own
    # transaction handler and the lifetime of the transaction is tied to the
    # lifetime of the request.
    config.add_settings({
        "tm.manager_hook":
        lambda request: transaction.TransactionManager(),
        "tm.activate_hook":
        activate_hook,
        "tm.annotate_user":
        False,
    })
    config.include("pyramid_tm")

    # Register support for services
    config.include("pyramid_services")

    # Register support for XMLRPC and override it's renderer to allow
    # specifying custom dumps arguments.
    config.include("pyramid_rpc.xmlrpc")
    config.add_renderer("xmlrpc", XMLRPCRenderer(allow_none=True))

    # Register support for our legacy action URLs
    config.include(".legacy.action_routing")

    # Register support for our domain predicates
    config.include(".domain")

    # Register support for template views.
    config.add_directive("add_template_view", template_view, action_wrap=False)

    # Register support for internationalization and localization
    config.include(".i18n")

    # Register the configuration for the PostgreSQL database.
    config.include(".db")

    # Register support for our rate limiting mechanisms
    config.include(".rate_limiting")

    config.include(".static")

    config.include(".policy")

    config.include(".search")

    # Register the support for AWS and Google Cloud
    config.include(".aws")
    config.include(".gcloud")

    # Register the support for Celery Tasks
    config.include(".tasks")

    # Register our session support
    config.include(".sessions")

    # Register our support for http and origin caching
    config.include(".cache.http")
    config.include(".cache.origin")

    # Register our authentication support.
    config.include(".accounts")

    # Register logged-in views
    config.include(".manage")

    # Allow the packaging app to register any services it has.
    config.include(".packaging")

    # Configure redirection support
    config.include(".redirects")

    # Register all our URL routes for Warehouse.
    config.include(".routes")

    # Include our admin application
    config.include(".admin")

    # Register forklift, at least until we split it out into it's own project.
    config.include(".forklift")

    # Block non HTTPS requests for the legacy ?:action= routes when they are
    # sent via POST.
    config.add_tween("warehouse.config.require_https_tween_factory")

    # Enable compression of our HTTP responses
    config.add_tween(
        "warehouse.utils.compression.compression_tween_factory",
        over=[
            "warehouse.cache.http.conditional_http_tween_factory",
            "pyramid_debugtoolbar.toolbar_tween_factory",
            "warehouse.raven.raven_tween_factory",
            EXCVIEW,
        ],
    )

    # Enable Warehouse to serve our static files
    prevent_http_cache = \
        config.get_settings().get("pyramid.prevent_http_cache", False)
    config.add_static_view(
        "static",
        "warehouse:static/dist/",
        # Don't cache at all if prevent_http_cache is true, else we'll cache
        # the files for 10 years.
        cache_max_age=0 if prevent_http_cache else 10 * 365 * 24 * 60 * 60,
    )
    config.add_cache_buster(
        "warehouse:static/dist/",
        ManifestCacheBuster(
            "warehouse:static/dist/manifest.json",
            reload=config.registry.settings["pyramid.reload_assets"],
            strict=not prevent_http_cache,
        ),
    )
    config.whitenoise_serve_static(
        autorefresh=prevent_http_cache,
        max_age=0 if prevent_http_cache else 10 * 365 * 24 * 60 * 60,
        manifest="warehouse:static/dist/manifest.json",
    )
    config.whitenoise_add_files("warehouse:static/dist/", prefix="/static/")

    # Enable Warehouse to serve our locale files
    config.add_static_view("locales", "warehouse:locales/")

    # Enable support of passing certain values like remote host, client
    # address, and protocol support in from an outer proxy to the application.
    config.add_wsgi_middleware(
        ProxyFixer,
        token=config.registry.settings["warehouse.token"],
        num_proxies=config.registry.settings.get("warehouse.num_proxies", 1),
    )

    # Protect against cache poisoning via the X-Vhm-Root headers.
    config.add_wsgi_middleware(VhmRootRemover)

    # Fix our host header when getting sent upload.pypi.io as a HOST.
    # TODO: Remove this, this is at the wrong layer.
    config.add_wsgi_middleware(HostRewrite)

    # We want Raven to be the last things we add here so that it's the outer
    # most WSGI middleware.
    config.include(".raven")

    # Register Content-Security-Policy service
    config.include(".csp")

    # Register Referrer-Policy service
    config.include(".referrer_policy")

    # Register recaptcha service
    config.include(".recaptcha")

    config.add_settings({
        "http": {
            "verify": "/etc/ssl/certs/",
        },
    })
    config.include(".http")

    # Add our theme if one was configured
    if config.get_settings().get("warehouse.theme"):
        config.include(config.get_settings()["warehouse.theme"])

    # Scan everything for configuration
    config.scan(ignore=[
        "warehouse.migrations.env",
        "warehouse.celery",
        "warehouse.wsgi",
    ], )

    # Finally, commit all of our changes
    config.commit()

    return config
示例#2
0
def configure(settings=None):
    if settings is None:
        settings = {}

    # Add information about the current copy of the code.
    maybe_set(settings, "warehouse.commit", "SOURCE_COMMIT", default="null")

    # Set the environment from an environment variable, if one hasn't already
    # been set.
    maybe_set(
        settings,
        "warehouse.env",
        "WAREHOUSE_ENV",
        Environment,
        default=Environment.production,
    )

    # Pull in default configuration from the environment.
    maybe_set(settings, "warehouse.token", "WAREHOUSE_TOKEN")
    maybe_set(settings, "warehouse.num_proxies", "WAREHOUSE_NUM_PROXIES", int)
    maybe_set(settings, "warehouse.domain", "WAREHOUSE_DOMAIN")
    maybe_set(settings, "forklift.domain", "FORKLIFT_DOMAIN")
    maybe_set(settings, "warehouse.legacy_domain", "WAREHOUSE_LEGACY_DOMAIN")
    maybe_set(settings, "site.name", "SITE_NAME", default="Warehouse")
    maybe_set(settings, "aws.key_id", "AWS_ACCESS_KEY_ID")
    maybe_set(settings, "aws.secret_key", "AWS_SECRET_ACCESS_KEY")
    maybe_set(settings, "aws.region", "AWS_REGION")
    maybe_set(settings, "gcloud.credentials", "GCLOUD_CREDENTIALS")
    maybe_set(settings, "gcloud.project", "GCLOUD_PROJECT")
    maybe_set(settings, "warehouse.release_files_table",
              "WAREHOUSE_RELEASE_FILES_TABLE")
    maybe_set(settings, "github.token", "GITHUB_TOKEN")
    maybe_set(
        settings,
        "github.token_scanning_meta_api.url",
        "GITHUB_TOKEN_SCANNING_META_API_URL",
        default="https://api.github.com/meta/public_keys/token_scanning",
    )
    maybe_set(settings, "warehouse.trending_table", "WAREHOUSE_TRENDING_TABLE")
    maybe_set(settings, "celery.broker_url", "BROKER_URL")
    maybe_set(settings, "celery.result_url", "REDIS_URL")
    maybe_set(settings, "celery.scheduler_url", "REDIS_URL")
    maybe_set(settings, "oidc.jwk_cache_url", "REDIS_URL")
    maybe_set(settings, "database.url", "DATABASE_URL")
    maybe_set(settings, "elasticsearch.url", "ELASTICSEARCH_URL")
    maybe_set(settings, "elasticsearch.url", "ELASTICSEARCH_SIX_URL")
    maybe_set(settings, "sentry.dsn", "SENTRY_DSN")
    maybe_set(settings, "sentry.transport", "SENTRY_TRANSPORT")
    maybe_set(settings, "sessions.url", "REDIS_URL")
    maybe_set(settings, "ratelimit.url", "REDIS_URL")
    maybe_set(settings, "sessions.secret", "SESSION_SECRET")
    maybe_set(settings, "camo.url", "CAMO_URL")
    maybe_set(settings, "camo.key", "CAMO_KEY")
    maybe_set(settings, "docs.url", "DOCS_URL")
    maybe_set(settings, "ga.tracking_id", "GA_TRACKING_ID")
    maybe_set(settings, "statuspage.url", "STATUSPAGE_URL")
    maybe_set(settings, "token.password.secret", "TOKEN_PASSWORD_SECRET")
    maybe_set(settings, "token.email.secret", "TOKEN_EMAIL_SECRET")
    maybe_set(settings, "token.two_factor.secret", "TOKEN_TWO_FACTOR_SECRET")
    maybe_set(
        settings,
        "warehouse.xmlrpc.search.enabled",
        "WAREHOUSE_XMLRPC_SEARCH",
        coercer=distutils.util.strtobool,
        default=True,
    )
    maybe_set(settings, "warehouse.xmlrpc.cache.url", "REDIS_URL")
    maybe_set(
        settings,
        "warehouse.xmlrpc.client.ratelimit_string",
        "XMLRPC_RATELIMIT_STRING",
        default="3600 per hour",
    )
    maybe_set(settings,
              "token.password.max_age",
              "TOKEN_PASSWORD_MAX_AGE",
              coercer=int)
    maybe_set(settings,
              "token.email.max_age",
              "TOKEN_EMAIL_MAX_AGE",
              coercer=int)
    maybe_set(
        settings,
        "token.two_factor.max_age",
        "TOKEN_TWO_FACTOR_MAX_AGE",
        coercer=int,
        default=300,
    )
    maybe_set(
        settings,
        "token.default.max_age",
        "TOKEN_DEFAULT_MAX_AGE",
        coercer=int,
        default=21600,  # 6 hours
    )
    maybe_set_compound(settings, "files", "backend", "FILES_BACKEND")
    maybe_set_compound(settings, "simple", "backend", "SIMPLE_BACKEND")
    maybe_set_compound(settings, "docs", "backend", "DOCS_BACKEND")
    maybe_set_compound(settings, "sponsorlogos", "backend",
                       "SPONSORLOGOS_BACKEND")
    maybe_set_compound(settings, "origin_cache", "backend", "ORIGIN_CACHE")
    maybe_set_compound(settings, "mail", "backend", "MAIL_BACKEND")
    maybe_set_compound(settings, "metrics", "backend", "METRICS_BACKEND")
    maybe_set_compound(settings, "breached_passwords", "backend",
                       "BREACHED_PASSWORDS")
    maybe_set_compound(settings, "malware_check", "backend",
                       "MALWARE_CHECK_BACKEND")

    # Pythondotorg integration settings
    maybe_set(settings,
              "pythondotorg.host",
              "PYTHONDOTORG_HOST",
              default="python.org")
    maybe_set(settings, "pythondotorg.api_token", "PYTHONDOTORG_API_TOKEN")

    # Configure our ratelimiters
    maybe_set(
        settings,
        "warehouse.account.user_login_ratelimit_string",
        "USER_LOGIN_RATELIMIT_STRING",
        default="10 per 5 minutes",
    )
    maybe_set(
        settings,
        "warehouse.account.ip_login_ratelimit_string",
        "IP_LOGIN_RATELIMIT_STRING",
        default="10 per 5 minutes",
    )
    maybe_set(
        settings,
        "warehouse.account.global_login_ratelimit_string",
        "GLOBAL_LOGIN_RATELIMIT_STRING",
        default="1000 per 5 minutes",
    )
    maybe_set(
        settings,
        "warehouse.account.email_add_ratelimit_string",
        "EMAIL_ADD_RATELIMIT_STRING",
        default="2 per day",
    )
    maybe_set(
        settings,
        "warehouse.account.password_reset_ratelimit_string",
        "PASSWORD_RESET_RATELIMIT_STRING",
        default="5 per day",
    )
    maybe_set(
        settings,
        "warehouse.manage.oidc.user_registration_ratelimit_string",
        "USER_OIDC_REGISTRATION_RATELIMIT_STRING",
        default="20 per day",
    )
    maybe_set(
        settings,
        "warehouse.manage.oidc.ip_registration_ratelimit_string",
        "IP_OIDC_REGISTRATION_RATELIMIT_STRING",
        default="20 per day",
    )

    # 2FA feature flags
    maybe_set(
        settings,
        "warehouse.two_factor_requirement.enabled",
        "TWOFACTORREQUIREMENT_ENABLED",
        coercer=distutils.util.strtobool,
        default=False,
    )
    maybe_set(
        settings,
        "warehouse.two_factor_mandate.available",
        "TWOFACTORMANDATE_AVAILABLE",
        coercer=distutils.util.strtobool,
        default=False,
    )
    maybe_set(
        settings,
        "warehouse.two_factor_mandate.enabled",
        "TWOFACTORMANDATE_ENABLED",
        coercer=distutils.util.strtobool,
        default=False,
    )

    # OIDC feature flags
    maybe_set(
        settings,
        "warehouse.oidc.enabled",
        "OIDC_ENABLED",
        coercer=distutils.util.strtobool,
        default=False,
    )

    # Add the settings we use when the environment is set to development.
    if settings["warehouse.env"] == Environment.development:
        settings.setdefault("enforce_https", False)
        settings.setdefault("pyramid.reload_assets", True)
        settings.setdefault("pyramid.reload_templates", True)
        settings.setdefault("pyramid.prevent_http_cache", True)
        settings.setdefault("debugtoolbar.hosts", ["0.0.0.0/0"])
        settings.setdefault(
            "debugtoolbar.panels",
            [
                ".".join(["pyramid_debugtoolbar.panels", panel]) for panel in [
                    "versions.VersionDebugPanel",
                    "settings.SettingsDebugPanel",
                    "headers.HeaderDebugPanel",
                    "request_vars.RequestVarsDebugPanel",
                    "renderings.RenderingsDebugPanel",
                    "logger.LoggingPanel",
                    "performance.PerformanceDebugPanel",
                    "routes.RoutesDebugPanel",
                    "sqla.SQLADebugPanel",
                    "tweens.TweensDebugPanel",
                    "introspection.IntrospectionDebugPanel",
                ]
            ],
        )

    # Actually setup our Pyramid Configurator with the values pulled in from
    # the environment as well as the ones passed in to the configure function.
    config = Configurator(settings=settings)
    config.set_root_factory(RootFactory)

    # Register support for services
    config.include("pyramid_services")

    # Register metrics
    config.include(".metrics")

    # Register our CSRF support. We do this here, immediately after we've
    # created the Configurator instance so that we ensure to get our defaults
    # set ASAP before anything else has a chance to set them and possibly call
    # Configurator().commit()
    config.include(".csrf")

    # Include anything needed by the development environment.
    if config.registry.settings["warehouse.env"] == Environment.development:
        config.include("pyramid_debugtoolbar")

    # Register our logging support
    config.include(".logging")

    # We'll want to use Jinja2 as our template system.
    config.include("pyramid_jinja2")

    # Include our filters
    config.include(".filters")

    # Including pyramid_mailer for sending emails through SMTP.
    config.include("pyramid_mailer")

    # We want to use newstyle gettext
    config.add_settings({"jinja2.newstyle": True})

    # Our translation strings are all in the "messages" domain
    config.add_settings({"jinja2.i18n.domain": "messages"})

    # We also want to use Jinja2 for .html templates as well, because we just
    # assume that all templates will be using Jinja.
    config.add_jinja2_renderer(".html")

    # Sometimes our files are .txt files and we still want to use Jinja2 to
    # render them.
    config.add_jinja2_renderer(".txt")

    # Anytime we want to render a .xml template, we'll also use Jinja.
    config.add_jinja2_renderer(".xml")

    # We need to enable our Client Side Include extension
    config.get_settings().setdefault(
        "jinja2.extensions",
        ["warehouse.utils.html.ClientSideIncludeExtension"])

    # We'll want to configure some filters for Jinja2 as well.
    filters = config.get_settings().setdefault("jinja2.filters", {})
    filters.setdefault("format_classifiers",
                       "warehouse.filters:format_classifiers")
    filters.setdefault("classifier_id", "warehouse.filters:classifier_id")
    filters.setdefault("format_tags", "warehouse.filters:format_tags")
    filters.setdefault("json", "warehouse.filters:tojson")
    filters.setdefault("camoify", "warehouse.filters:camoify")
    filters.setdefault("shorten_number", "warehouse.filters:shorten_number")
    filters.setdefault("urlparse", "warehouse.filters:urlparse")
    filters.setdefault("contains_valid_uris",
                       "warehouse.filters:contains_valid_uris")
    filters.setdefault("format_package_type",
                       "warehouse.filters:format_package_type")
    filters.setdefault("parse_version", "warehouse.filters:parse_version")
    filters.setdefault("localize_datetime",
                       "warehouse.filters:localize_datetime")
    filters.setdefault("is_recent", "warehouse.filters:is_recent")

    # We also want to register some global functions for Jinja
    jglobals = config.get_settings().setdefault("jinja2.globals", {})
    jglobals.setdefault("is_valid_uri", "warehouse.utils.http:is_valid_uri")
    jglobals.setdefault("gravatar", "warehouse.utils.gravatar:gravatar")
    jglobals.setdefault("gravatar_profile", "warehouse.utils.gravatar:profile")
    jglobals.setdefault("now", "warehouse.utils:now")

    # And some enums to reuse in the templates
    jglobals.setdefault("AdminFlagValue",
                        "warehouse.admin.flags:AdminFlagValue")
    jglobals.setdefault(
        "OrganizationInvitationStatus",
        "warehouse.organizations.models:OrganizationInvitationStatus",
    )
    jglobals.setdefault("OrganizationRoleType",
                        "warehouse.organizations.models:OrganizationRoleType")
    jglobals.setdefault("OrganizationType",
                        "warehouse.organizations.models:OrganizationType")
    jglobals.setdefault("RoleInvitationStatus",
                        "warehouse.packaging.models:RoleInvitationStatus")

    # We'll store all of our templates in one location, warehouse/templates
    # so we'll go ahead and add that to the Jinja2 search path.
    config.add_jinja2_search_path("warehouse:templates", name=".html")
    config.add_jinja2_search_path("warehouse:templates", name=".txt")
    config.add_jinja2_search_path("warehouse:templates", name=".xml")

    # We want to configure our JSON renderer to sort the keys, and also to use
    # an ultra compact serialization format.
    config.add_renderer("json",
                        renderers.JSON(sort_keys=True, separators=(",", ":")))

    # Configure retry support.
    config.add_settings({"retry.attempts": 3})
    config.include("pyramid_retry")

    # Configure our transaction handling so that each request gets its own
    # transaction handler and the lifetime of the transaction is tied to the
    # lifetime of the request.
    config.add_settings({
        "tm.manager_hook":
        lambda request: transaction.TransactionManager(),
        "tm.activate_hook":
        activate_hook,
        "tm.commit_veto":
        commit_veto,
        "tm.annotate_user":
        False,
    })
    config.include("pyramid_tm")

    # Register our XMLRPC service
    config.include(".legacy.api.xmlrpc")

    # Register our XMLRPC cache
    config.include(".legacy.api.xmlrpc.cache")

    # Register support for XMLRPC and override it's renderer to allow
    # specifying custom dumps arguments.
    config.include("pyramid_rpc.xmlrpc")
    config.add_renderer("xmlrpc", XMLRPCRenderer(allow_none=True))

    # Register support for our legacy action URLs
    config.include(".legacy.action_routing")

    # Register support for our custom predicates
    config.include(".predicates")

    # Register support for template views.
    config.add_directive("add_template_view", template_view, action_wrap=False)

    # Register support for internationalization and localization
    config.include(".i18n")

    # Register the configuration for the PostgreSQL database.
    config.include(".db")

    # Register the support for Celery Tasks
    config.include(".tasks")

    # Register support for our rate limiting mechanisms
    config.include(".rate_limiting")

    config.include(".static")

    config.include(".policy")

    config.include(".search")

    # Register the support for AWS and Google Cloud
    config.include(".aws")
    config.include(".gcloud")

    # Register our session support
    config.include(".sessions")

    # Register our support for http and origin caching
    config.include(".cache.http")
    config.include(".cache.origin")

    # Register support for sending emails
    config.include(".email")

    # Register our authentication support.
    config.include(".accounts")

    # Register support for Macaroon based authentication
    config.include(".macaroons")

    # Register support for OIDC provider based authentication
    config.include(".oidc")

    # Register support for malware checks
    config.include(".malware")

    # Register logged-in views
    config.include(".manage")

    # Register our organization support.
    config.include(".organizations")

    # Allow the packaging app to register any services it has.
    config.include(".packaging")

    # Configure redirection support
    config.include(".redirects")

    # Register all our URL routes for Warehouse.
    config.include(".routes")

    # Allow the sponsors app to list sponsors
    config.include(".sponsors")

    # Allow the banners app to list banners
    config.include(".banners")

    # Include our admin application
    config.include(".admin")

    # Register forklift, at least until we split it out into it's own project.
    config.include(".forklift")

    # Block non HTTPS requests for the legacy ?:action= routes when they are
    # sent via POST.
    config.add_tween("warehouse.config.require_https_tween_factory")

    # Enable compression of our HTTP responses
    config.add_tween(
        "warehouse.utils.compression.compression_tween_factory",
        over=[
            "warehouse.cache.http.conditional_http_tween_factory",
            "pyramid_debugtoolbar.toolbar_tween_factory",
            EXCVIEW,
        ],
    )

    # Enable Warehouse to serve our static files
    prevent_http_cache = config.get_settings().get(
        "pyramid.prevent_http_cache", False)
    config.add_static_view(
        "static",
        "warehouse:static/dist/",
        # Don't cache at all if prevent_http_cache is true, else we'll cache
        # the files for 10 years.
        cache_max_age=0 if prevent_http_cache else 10 * 365 * 24 * 60 * 60,
    )
    config.add_cache_buster(
        "warehouse:static/dist/",
        ManifestCacheBuster(
            "warehouse:static/dist/manifest.json",
            reload=config.registry.settings["pyramid.reload_assets"],
            strict=not prevent_http_cache,
        ),
    )
    config.whitenoise_serve_static(
        autorefresh=prevent_http_cache,
        max_age=0 if prevent_http_cache else 10 * 365 * 24 * 60 * 60,
    )
    config.whitenoise_add_files("warehouse:static/dist/", prefix="/static/")
    config.whitenoise_add_manifest("warehouse:static/dist/manifest.json",
                                   prefix="/static/")

    # Enable support of passing certain values like remote host, client
    # address, and protocol support in from an outer proxy to the application.
    config.add_wsgi_middleware(
        ProxyFixer,
        token=config.registry.settings["warehouse.token"],
        num_proxies=config.registry.settings.get("warehouse.num_proxies", 1),
    )

    # Protect against cache poisoning via the X-Vhm-Root headers.
    config.add_wsgi_middleware(VhmRootRemover)

    # We want Sentry to be the last things we add here so that it's the outer
    # most WSGI middleware.
    config.include(".sentry")

    # Register Content-Security-Policy service
    config.include(".csp")

    # Register Referrer-Policy service
    config.include(".referrer_policy")

    config.add_settings({"http": {"verify": "/etc/ssl/certs/"}})
    config.include(".http")

    # Scan everything for configuration
    config.scan(
        categories=(
            "pyramid",
            "warehouse",
        ),
        ignore=[
            "warehouse.migrations.env", "warehouse.celery", "warehouse.wsgi"
        ],
    )

    # Sanity check our request and responses.
    # Note: It is very important that this go last. We need everything else that might
    #       have added a tween to be registered prior to this.
    config.include(".sanity")

    # Finally, commit all of our changes
    config.commit()

    return config
示例#3
0
def configure(settings=None):
    if settings is None:
        settings = {}

    # Add information about the current copy of the code.
    settings.setdefault("warehouse.commit", __commit__)

    # Set the environment from an environment variable, if one hasn't already
    # been set.
    maybe_set(
        settings,
        "warehouse.env",
        "WAREHOUSE_ENV",
        Environment,
        default=Environment.production,
    )

    # Pull in default configuration from the environment.
    maybe_set(settings, "warehouse.token", "WAREHOUSE_TOKEN")
    maybe_set(settings, "warehouse.theme", "WAREHOUSE_THEME")
    maybe_set(settings, "site.name", "SITE_NAME", default="Warehouse")
    maybe_set(settings, "aws.key_id", "AWS_ACCESS_KEY_ID")
    maybe_set(settings, "aws.secret_key", "AWS_SECRET_ACCESS_KEY")
    maybe_set(settings, "aws.region", "AWS_REGION")
    maybe_set(settings, "celery.broker_url", "AMQP_URL")
    maybe_set(settings, "celery.result_url", "REDIS_URL")
    maybe_set(settings, "csp.report_uri", "CSP_REPORT_URI")
    maybe_set(settings, "database.url", "DATABASE_URL")
    maybe_set(settings, "elasticsearch.url", "ELASTICSEARCH_URL")
    maybe_set(settings, "sentry.dsn", "SENTRY_DSN")
    maybe_set(settings, "sentry.transport", "SENTRY_TRANSPORT")
    maybe_set(settings, "sessions.url", "REDIS_URL")
    maybe_set(settings, "download_stats.url", "REDIS_URL")
    maybe_set(settings, "sessions.secret", "SESSION_SECRET")
    maybe_set(settings, "camo.url", "CAMO_URL")
    maybe_set(settings, "camo.key", "CAMO_KEY")
    maybe_set(settings, "docs.url", "DOCS_URL")
    maybe_set_compound(settings, "files", "backend", "FILES_BACKEND")
    maybe_set_compound(settings, "origin_cache", "backend", "ORIGIN_CACHE")

    # Add the settings we use when the environment is set to development.
    if settings["warehouse.env"] == Environment.development:
        settings.setdefault("enforce_https", False)
        settings.setdefault("pyramid.reload_assets", True)
        settings.setdefault("pyramid.reload_templates", True)
        settings.setdefault("pyramid.prevent_http_cache", True)
        settings.setdefault("debugtoolbar.hosts", ["0.0.0.0/0"])
        settings.setdefault(
            "debugtoolbar.panels",
            [
                ".".join(["pyramid_debugtoolbar.panels", panel]) for panel in [
                    "versions.VersionDebugPanel",
                    "settings.SettingsDebugPanel",
                    "headers.HeaderDebugPanel",
                    "request_vars.RequestVarsDebugPanel",
                    "renderings.RenderingsDebugPanel",
                    "logger.LoggingPanel",
                    "performance.PerformanceDebugPanel",
                    "routes.RoutesDebugPanel",
                    "sqla.SQLADebugPanel",
                    "tweens.TweensDebugPanel",
                    "introspection.IntrospectionDebugPanel",
                ]
            ],
        )

    # Actually setup our Pyramid Configurator with the values pulled in from
    # the environment as well as the ones passed in to the configure function.
    config = Configurator(settings=settings)

    # Include anything needed by the development environment.
    if config.registry.settings["warehouse.env"] == Environment.development:
        config.include("pyramid_debugtoolbar")

    # Register our logging support
    config.include(".logging")

    # We'll want to use Jinja2 as our template system.
    config.include("pyramid_jinja2")

    # We want to use newstyle gettext
    config.add_settings({"jinja2.newstyle": True})

    # We also want to use Jinja2 for .html templates as well, because we just
    # assume that all templates will be using Jinja.
    config.add_jinja2_renderer(".html")

    # Sometimes our files are .txt files and we still want to use Jinja2 to
    # render them.
    config.add_jinja2_renderer(".txt")

    # Anytime we want to render a .xml template, we'll also use Jinja.
    config.add_jinja2_renderer(".xml")

    # We'll want to configure some filters for Jinja2 as well.
    filters = config.get_settings().setdefault("jinja2.filters", {})
    filters.setdefault("format_tags", "warehouse.filters:format_tags")
    filters.setdefault("json", "warehouse.filters:tojson")
    filters.setdefault("readme", "warehouse.filters:readme")
    filters.setdefault("shorten_number", "warehouse.filters:shorten_number")
    filters.setdefault("urlparse", "warehouse.filters:urlparse")

    # We also want to register some global functions for Jinja
    jglobals = config.get_settings().setdefault("jinja2.globals", {})
    jglobals.setdefault("gravatar", "warehouse.utils.gravatar:gravatar")
    jglobals.setdefault("html_include", "warehouse.utils.html:html_include")
    jglobals.setdefault("now", "warehouse.utils:now")

    # We'll store all of our templates in one location, warehouse/templates
    # so we'll go ahead and add that to the Jinja2 search path.
    config.add_jinja2_search_path("warehouse:templates", name=".html")
    config.add_jinja2_search_path("warehouse:templates", name=".txt")
    config.add_jinja2_search_path("warehouse:templates", name=".xml")

    # We want to configure our JSON renderer to sort the keys, and also to use
    # an ultra compact serialization format.
    config.add_renderer(
        "json",
        renderers.JSON(sort_keys=True, separators=(",", ":")),
    )

    # Configure our transaction handling so that each request gets its own
    # transaction handler and the lifetime of the transaction is tied to the
    # lifetime of the request.
    config.add_settings({
        "tm.attempts":
        3,
        "tm.manager_hook":
        lambda request: transaction.TransactionManager(),
        "tm.activate_hook":
        activate_hook,
        "tm.annotate_user":
        False,
    })
    config.include("pyramid_tm")

    # Register support for services
    config.include("pyramid_services")

    # Register our find_service_factory methods
    config.add_request_method(find_service_factory)
    config.add_directive("find_service_factory", find_service_factory)

    # Register support for XMLRPC and override it's renderer to allow
    # specifying custom dumps arguments.
    config.include("pyramid_rpc.xmlrpc")
    config.add_renderer("xmlrpc", XMLRPCRenderer(allow_none=True))

    # Register support for our legacy action URLs
    config.include(".legacy.action_routing")

    # Register support for internationalization and localization
    config.include(".i18n")

    # Register the configuration for the PostgreSQL database.
    config.include(".db")

    config.include(".search")

    # Register the support for AWS
    config.include(".aws")

    # Register the support for Celery
    config.include(".celery")

    # Register our session support
    config.include(".sessions")

    # Register our support for http and origin caching
    config.include(".cache.http")
    config.include(".cache.origin")

    # Register our CSRF support
    config.include(".csrf")

    # Register our authentication support.
    config.include(".accounts")

    # Allow the packaging app to register any services it has.
    config.include(".packaging")

    # Configure redirection support
    config.include(".redirects")

    # Register all our URL routes for Warehouse.
    config.include(".routes")

    # Enable a Content Security Policy
    config.add_settings({
        "csp": {
            "connect-src": ["'self'"],
            "default-src": ["'none'"],
            "font-src": ["'self'", "fonts.gstatic.com"],
            "frame-ancestors": ["'none'"],
            "img-src": [
                "'self'",
                config.registry.settings["camo.url"],
                "https://secure.gravatar.com",
            ],
            "referrer": ["origin-when-cross-origin"],
            "reflected-xss": ["block"],
            "report-uri": [config.registry.settings.get("csp.report_uri")],
            "script-src": ["'self'"],
            "style-src": ["'self'", "fonts.googleapis.com"],
        },
    })
    config.add_tween("warehouse.config.content_security_policy_tween_factory")

    # Block non HTTPS requests for the legacy ?:action= routes when they are
    # sent via POST.
    config.add_tween("warehouse.config.require_https_tween_factory")

    # Enable compression of our HTTP responses
    config.add_tween(
        "warehouse.utils.compression.compression_tween_factory",
        over=[
            "warehouse.cache.http.conditional_http_tween_factory",
            "pyramid_debugtoolbar.toolbar_tween_factory",
            "warehouse.raven.raven_tween_factory",
            EXCVIEW,
        ],
    )

    # Enable Warehouse to serve our static files
    prevent_http_cache = \
        config.get_settings().get("pyramid.prevent_http_cache", False)
    config.add_static_view(
        "static",
        "warehouse:static/dist/",
        # Don't cache at all if prevent_http_cache is true, else we'll cache
        # the files for 10 years.
        cache_max_age=0 if prevent_http_cache else 10 * 365 * 24 * 60 * 60,
    )
    config.add_cache_buster(
        "warehouse:static/dist/",
        ManifestCacheBuster(
            "warehouse:static/dist/manifest.json",
            reload=config.registry.settings["pyramid.reload_assets"],
            strict=not prevent_http_cache,
        ),
    )

    # Enable Warehouse to serve our locale files
    config.add_static_view("locales", "warehouse:locales/")

    # Enable support of passing certain values like remote host, client
    # address, and protocol support in from an outer proxy to the application.
    config.add_wsgi_middleware(
        ProxyFixer,
        token=config.registry.settings["warehouse.token"],
    )

    # Protect against cache poisoning via the X-Vhm-Root headers.
    config.add_wsgi_middleware(VhmRootRemover)

    # We want Raven to be the last things we add here so that it's the outer
    # most WSGI middleware.
    config.include(".raven")

    # Add our theme if one was configured
    if config.get_settings().get("warehouse.theme"):
        config.include(config.get_settings()["warehouse.theme"])

    # Scan everything for configuration
    config.scan(ignore=["warehouse.migrations.env", "warehouse.wsgi"])

    return config
示例#4
0
def includeme(config):
    """
    Pyramid includeme file for the :class:`pyramid.config.Configurator`
    """
    settings = config.registry.settings

    # config.add_renderer('json', JSONP())
    # release file download
    config.add_renderer('repository', dl_renderer_factory)

    # Jinja configuration
    # We don't use jinja2 filename, .html instead
    config.add_renderer('.html', renderer_factory)
    # helpers
    config.add_subscriber(add_urlhelpers, IBeforeRender)
    # i18n
    config.add_translation_dirs('locale/')

    pypi_url = settings.get('pyshop.pypi.url', 'https://pypi.python.org/pypi')
    # PyPI url for XML RPC service consume
    pypi.set_proxy(pypi_url, settings.get('pyshop.pypi.transport_proxy'))

    # Javascript + Media
    config.add_static_view('static', 'static', cache_max_age=3600)
    # config.add_static_view('repository', 'repository', cache_max_age=3600)

    config.add_route(u'login', u'/login',)
    config.add_view(u'pyshop.views.credentials.Login',
                    route_name=u'login',
                    renderer=u'shared/login.html')

    config.add_route(u'logout', u'/logout')
    config.add_view(u'pyshop.views.credentials.Logout',
                    route_name=u'logout',
                    permission=u'user_view')

    # Home page
    config.add_route(u'index', u'/')
    config.add_view(u'pyshop.views.Index',
                    route_name=u'index',
                    permission=u'user_view')

    # Archive downloads
    config.add_route(u'show_external_release_file',
                     u'/repository/ext/{release_id}/{filename:.*}',
                     request_method=u'GET')
    config.add_view(u'pyshop.views.repository.show_external_release_file',
                    route_name=u'show_external_release_file',
                    renderer=u'repository',
                    permission=u'download_releasefile')

    config.add_route(u'show_release_file',
                     u'/repository/{file_id}/{filename:.*}',
                     request_method=u'GET')
    config.add_view(u'pyshop.views.repository.show_release_file',
                    route_name=u'show_release_file',
                    renderer=u'repository',
                    permission=u'download_releasefile')

    # Simple views used by pip
    config.add_route(u'list_simple', u'/simple/', request_method=u'GET')

    config.add_view(u'pyshop.views.simple.List',
                    route_name=u'list_simple',
                    renderer=u'pyshop/simple/list.html',
                    permission=u'download_releasefile')

    config.add_route(u'show_simple', u'/simple/{package_name}/')
    config.add_view(u'pyshop.views.simple.Show',
                    route_name=u'show_simple',
                    renderer=u'pyshop/simple/show.html',
                    permission=u'download_releasefile')

    try:
        config.add_notfound_view(notfound, append_slash=True)
    except AttributeError:
        # Pyramid < 1.4
        pass


    # Used by setup.py sdist upload

    config.add_route(u'upload_releasefile', u'/simple/',
                     request_method=u'POST')

    config.add_view(u'pyshop.views.simple.UploadReleaseFile',
                    renderer=u'pyshop/simple/create.html',
                    route_name=u'upload_releasefile',
                    permission=u'upload_releasefile')

    # Web Services

    config.add_renderer('pyshopxmlrpc', XMLRPCRenderer(allow_none=True))
    config.add_xmlrpc_endpoint(
        'api', '/pypi/xmlrpc', default_renderer='pyshopxmlrpc')
    config.scan('pyshop.views.xmlrpc')

    # Backoffice Views

    config.add_route(u'list_package', u'/pyshop/package')
    config.add_view(u'pyshop.views.package.List',
                    route_name='list_package',
                    renderer=u'pyshop/package/list.html',
                    permission=u'user_view')

    config.add_route(u'list_package_page', u'/pyshop/package/p/{page_no}')
    config.add_view(u'pyshop.views.package.List',
                    route_name='list_package_page',
                    renderer=u'pyshop/package/list.html',
                    permission=u'user_view')

    config.add_route(u'show_package',
                     u'/pyshop/package/{package_name}')

    config.add_route(u'show_package_version',
                     u'/pyshop/package/{package_name}/{release_version}')

    config.add_view(u'pyshop.views.package.Show',
                    route_name=u'show_package',
                    renderer=u'pyshop/package/show.html',
                    permission=u'user_view')

    config.add_view(u'pyshop.views.package.Show',
                    route_name=u'show_package_version',
                    renderer=u'pyshop/package/show.html',
                    permission=u'user_view')

    # Admin  view
    config.add_route(u'list_account', u'/pyshop/account')
    config.add_view(u'pyshop.views.account.List',
                    route_name=u'list_account',
                    renderer=u'pyshop/account/list.html',
                    permission=u'admin_view')

    config.add_route(u'create_account', u'/pyshop/account/new')
    config.add_view(u'pyshop.views.account.Create',
                    route_name=u'create_account',
                    renderer=u'pyshop/account/create.html',
                    permission=u'admin_view')

    config.add_route(u'edit_account', u'/pyshop/account/{user_id}')
    config.add_view(u'pyshop.views.account.Edit',
                    route_name=u'edit_account',
                    renderer=u'pyshop/account/edit.html',
                    permission=u'admin_view')

    config.add_route(u'delete_account', u'/pyshop/delete/account/{user_id}')
    config.add_view(u'pyshop.views.account.Delete',
                    route_name=u'delete_account',
                    renderer=u'pyshop/account/delete.html',
                    permission=u'admin_view')

    config.add_route(u'purge_package', u'/pyshop/purge/package/{package_id}')
    config.add_view(u'pyshop.views.package.Purge',
                    route_name=u'purge_package',
                    renderer=u'pyshop/package/purge.html',
                    permission=u'admin_view')

    # Current user can update it's information
    config.add_route(u'edit_user', u'/pyshop/user')
    config.add_view(u'pyshop.views.user.Edit',
                    route_name=u'edit_user',
                    renderer=u'pyshop/user/edit.html',
                    permission=u'user_view')

    config.add_route(u'change_password', u'/pyshop/user/password')
    config.add_view(u'pyshop.views.user.ChangePassword',
                    route_name=u'change_password',
                    renderer=u'pyshop/user/change_password.html',
                    permission=u'user_view')

    # Credentials
    for route in ('list_simple', 'show_simple',
                  'show_release_file', 'show_external_release_file',
                  'upload_releasefile'):
        config.add_view('pyshop.views.credentials.authbasic',
                        route_name=route,
                        context='pyramid.exceptions.Forbidden'
                        )

    config.add_view('pyshop.views.credentials.Login',
                    renderer=u'shared/login.html',
                    context=u'pyramid.exceptions.Forbidden')
示例#5
0
def include_package(config):
    """Pyramid package include"""

    # add translations
    config.add_translation_dirs('pyams_zfiles:locales')

    # register new ZFiles permissions
    config.register_permission({
        'id': MANAGE_APPLICATION_PERMISSION,
        'title': _("Manage ZFiles application")
    })
    config.register_permission({
        'id': CREATE_DOCUMENT_PERMISSION,
        'title': _("Create new document")
    })
    config.register_permission({
        'id':
        CREATE_DOCUMENT_WITH_OWNER_PERMISSION,
        'title':
        _("Create new document (with specified owner)")
    })
    config.register_permission({
        'id': MANAGE_DOCUMENT_PERMISSION,
        'title': _("Manage document")
    })
    config.register_permission({
        'id': READ_DOCUMENT_PERMISSION,
        'title': _("Read document")
    })

    # upgrade system manager role
    config.upgrade_role(SYSTEM_ADMIN_ROLE,
                        permissions={
                            MANAGE_APPLICATION_PERMISSION,
                            CREATE_DOCUMENT_PERMISSION,
                            CREATE_DOCUMENT_WITH_OWNER_PERMISSION,
                            MANAGE_DOCUMENT_PERMISSION,
                            READ_DOCUMENT_PERMISSION
                        })

    # register new roles
    config.register_role({
        'id': ZFILES_ADMIN_ROLE,
        'title': _("ZFiles application manager (role)"),
        'permissions': {
            MANAGE_APPLICATION_PERMISSION, MANAGE_ROLES_PERMISSION,
            CREATE_DOCUMENT_WITH_OWNER_PERMISSION
        },
        'managers': {ADMIN_USER_ID,
                     ROLE_ID.format(SYSTEM_ADMIN_ROLE)}
    })
    config.register_role({
        'id': ZFILES_IMPORTER_ROLE,
        'title': _("Documents importer (role)"),
        'permissions': {
            CREATE_DOCUMENT_PERMISSION, CREATE_DOCUMENT_WITH_OWNER_PERMISSION,
            MANAGE_DOCUMENT_PERMISSION, READ_DOCUMENT_PERMISSION
        },
        'managers': {
            ADMIN_USER_ID,
            ROLE_ID.format(SYSTEM_ADMIN_ROLE),
            ROLE_ID.format(ZFILES_ADMIN_ROLE)
        }
    })
    config.register_role({
        'id': ZFILES_MANAGER_ROLE,
        'title': _("Document manager (role)"),
        'permissions': {
            CREATE_DOCUMENT_PERMISSION, MANAGE_DOCUMENT_PERMISSION,
            READ_DOCUMENT_PERMISSION
        },
        'managers': {
            ADMIN_USER_ID,
            ROLE_ID.format(SYSTEM_ADMIN_ROLE),
            ROLE_ID.format(ZFILES_ADMIN_ROLE)
        }
    })
    config.register_role({
        'id': ZFILES_CREATOR_ROLE,
        'title': _("Document creator (role)"),
        'managers': {
            ADMIN_USER_ID,
            ROLE_ID.format(SYSTEM_ADMIN_ROLE),
            ROLE_ID.format(ZFILES_ADMIN_ROLE)
        }
    })
    config.register_role({
        'id': ZFILES_OWNER_ROLE,
        'title': _("Document owner (role)"),
        'permissions': {MANAGE_DOCUMENT_PERMISSION, READ_DOCUMENT_PERMISSION},
        'managers': {
            ADMIN_USER_ID,
            ROLE_ID.format(SYSTEM_ADMIN_ROLE),
            ROLE_ID.format(ZFILES_ADMIN_ROLE)
        }
    })
    config.register_role({
        'id': ZFILES_READER_ROLE,
        'title': _("Document reader (role)"),
        'permissions': {READ_DOCUMENT_PERMISSION},
        'managers': {
            ADMIN_USER_ID,
            ROLE_ID.format(SYSTEM_ADMIN_ROLE),
            ROLE_ID.format(ZFILES_ADMIN_ROLE),
            ROLE_ID.format(ZFILES_OWNER_ROLE),
            ROLE_ID.format(ZFILES_MANAGER_ROLE)
        }
    })

    # register new REST API routes
    config.add_route(
        REST_CONTAINER_ROUTE,
        config.registry.settings.get('pyams.zfiles.rest_container_route',
                                     '/api/zfiles/rest'))
    config.add_route(
        REST_DOCUMENT_ROUTE,
        config.registry.settings.get('pyams.zfiles.rest_document_route',
                                     '/api/zfiles/rest/{oid}*version'))

    # register new GraphQL API route
    config.add_route(
        GRAPHQL_API_ROUTE,
        config.registry.settings.get('pyams.zfiles.graphql_route',
                                     '/api/zfiles/graphql'))

    # register new RPC endpoints
    config.add_jsonrpc_endpoint(
        JSONRPC_ENDPOINT,
        config.registry.settings.get('pyams.zfiles.jsonrpc_route',
                                     '/api/zfiles/jsonrpc'))

    config.add_renderer('xmlrpc-with-none', XMLRPCRenderer(allow_none=True))
    config.add_xmlrpc_endpoint(XMLRPC_ENDPOINT,
                               config.registry.settings.get(
                                   'pyams.zfiles.xmlrpc_route',
                                   '/api/zfiles/xmlrpc'),
                               default_renderer='xmlrpc-with-none')

    try:
        import pyams_zmi  # pylint: disable=import-outside-toplevel,unused-import
    except ImportError:
        config.scan(ignore='pyams_zfiles.zmi')
    else:
        config.scan()