diff --git a/config.py b/config.py index 43249828f..48216d1e3 100644 --- a/config.py +++ b/config.py @@ -196,9 +196,6 @@ class DefaultConfig(ImmutableConfig): # Feature Flag: Whether Google login is supported. FEATURE_GOOGLE_LOGIN = False - # Feature Flag: Whether Dex login is supported. - FEATURE_DEX_LOGIN = False - # Feature Flag: whether to enable support chat FEATURE_SUPPORT_CHAT = False diff --git a/util/config/schema.py b/util/config/schema.py index dca969ef5..7fcc11d9c 100644 --- a/util/config/schema.py +++ b/util/config/schema.py @@ -1,3 +1,89 @@ +# INTERNAL_ONLY_PROPERTIES defines the properties in the config that, while settable, should +# not be documented for external users. These will generally be used for internal test or only +# given to customers when they have been briefed on the side effects of using them. +INTERNAL_ONLY_PROPERTIES = { + '__module__', + '__doc__', + 'create_transaction', + + 'TESTING', + 'SEND_FILE_MAX_AGE_DEFAULT', + + 'REPLICATION_QUEUE_NAME', + 'DOCKERFILE_BUILD_QUEUE_NAME', + 'CHUNK_CLEANUP_QUEUE_NAME', + 'SECSCAN_NOTIFICATION_QUEUE_NAME', + 'SECURITY_SCANNER_ISSUER_NAME', + 'NOTIFICATION_QUEUE_NAME', + + 'FEATURE_BILLING', + 'FEATURE_SUPPORT_CHAT', + 'BILLING_TYPE', + + 'INSTANCE_SERVICE_KEY_LOCATION', + 'INSTANCE_SERVICE_KEY_REFRESH', + 'INSTANCE_SERVICE_KEY_SERVICE', + 'INSTANCE_SERVICE_KEY_KID_LOCATION', + 'INSTANCE_SERVICE_KEY_EXPIRATION', + 'UNAPPROVED_SERVICE_KEY_TTL_SEC', + 'EXPIRED_SERVICE_KEY_TTL_SEC', + 'REGISTRY_JWT_AUTH_MAX_FRESH_S', + + 'BITTORRENT_FILENAME_PEPPER', + 'BITTORRENT_WEBSEED_LIFETIME', + + 'SERVICE_LOG_ACCOUNT_ID', + 'BUILDLOGS_OPTIONS', + 'LIBRARY_NAMESPACE', + 'STAGGER_WORKERS', + 'QUEUE_WORKER_METRICS_REFRESH_SECONDS', + 'PUSH_TEMP_TAG_EXPIRATION_SEC', + 'GARBAGE_COLLECTION_FREQUENCY', + 'PAGE_TOKEN_KEY', + 'BUILD_MANAGER', + 'JWTPROXY_AUDIENCE', + 'SYSTEM_SERVICE_BLACKLIST', + 'JWTPROXY_SIGNER', + 'SECURITY_SCANNER_INDEXING_MIN_ID', + 'MAX_BUILD_QUEUE_RATE_SECS', + 'STATIC_SITE_BUCKET', + 'LABEL_KEY_RESERVED_PREFIXES', + 'TEAM_SYNC_WORKER_FREQUENCY', + 'DOCUMENTATION_METADATA', + 'DOCUMENTATION_LOCATION', + 'JSONIFY_PRETTYPRINT_REGULAR', + 'SYSTEM_LOGS_FILE', + 'SYSTEM_LOGS_PATH', + 'SYSTEM_SERVICES_PATH', + 'TUF_GUN_PREFIX', + 'LOGGING_LEVEL', + 'SIGNED_GRANT_EXPIRATION_SEC', + 'PROMETHEUS_AGGREGATOR_URL', + 'DB_TRANSACTION_FACTORY', + 'NOTIFICATION_SEND_TIMEOUT', + 'QUEUE_METRICS_TYPE', + 'MAIL_FAIL_SILENTLY', + 'LOCAL_OAUTH_HANDLER', + 'USE_CDN', + 'MAX_BUILD_QUEUE_RATE_ITEMS', + 'ANALYTICS_TYPE', + + 'EXCEPTION_LOG_TYPE', + 'SENTRY_DSN', + 'SENTRY_PUBLIC_DSN', + + 'SECURITY_SCANNER_ENDPOINT_BATCH', + 'SECURITY_SCANNER_API_TIMEOUT_SECONDS', + 'SECURITY_SCANNER_API_TIMEOUT_POST_SECONDS', + 'SECURITY_SCANNER_ENGINE_VERSION_TARGET', + 'SECURITY_SCANNER_READONLY_FAILOVER_ENDPOINTS', + 'SECURITY_SCANNER_API_VERSION', + + # TODO: move this into the schema once we support signing in QE. + 'FEATURE_SIGNING', + 'TUF_SERVER', +} + CONFIG_SCHEMA = { 'type': 'object', 'description': 'Schema for Quay configuration', @@ -99,17 +185,17 @@ CONFIG_SCHEMA = { 'x-example': 588, }, 'MAIL_USERNAME': { - 'type': 'string', + 'type': ['string', 'null'], 'description': 'The SMTP username to use when sending e-mails.', 'x-example': 'myuser', }, 'MAIL_PASSWORD': { - 'type': 'string', + 'type': ['string', 'null'], 'description': 'The SMTP password to use when sending e-mails.', 'x-example': 'mypassword', }, 'MAIL_DEFAULT_SENDER': { - 'type': 'string', + 'type': ['string', 'null'], 'description': 'If specified, the e-mail address used as the `from` when Quay sends e-mails. If none, defaults to `support@quay.io`.', 'x-example': 'support@myco.com', }, @@ -121,6 +207,33 @@ CONFIG_SCHEMA = { 'x-example': 'mysql+pymysql://username:password@dns.of.database/quay', 'x-reference': 'https://www.postgresql.org/docs/9.3/static/libpq-connect.html#AEN39495', }, + 'DB_CONNECTION_ARGS': { + 'type': 'object', + 'description': 'If specified, connection arguments for the database such as timeouts and SSL.', + 'properties': { + 'threadlocals': { + 'type': 'boolean', + 'description': 'Whether to use thread-local connections. Should *ALWAYS* be `true`' + }, + 'autorollback': { + 'type': 'boolean', + 'description': 'Whether to use auto-rollback connections. Should *ALWAYS* be `true`' + }, + 'ssl': { + 'type': 'object', + 'description': 'SSL connection configuration', + 'properties': { + 'ca': { + 'type': 'string', + 'description': '*Absolute container path* to the CA certificate to use for SSL connections', + 'x-example': 'conf/stack/ssl-ca-cert.pem', + }, + }, + 'required': ['ca'], + }, + }, + 'required': ['threadlocals', 'autorollback'], + }, 'ALLOW_PULLS_WITHOUT_STRICT_LOGGING': { 'type': 'boolean', 'description': 'If true, pulls in which the pull audit log entry cannot be written will ' + @@ -179,6 +292,40 @@ CONFIG_SCHEMA = { }, 'x-example': ['s3_us_east', 's3_us_west'], }, + 'USERFILES_LOCATION': { + 'type': 'string', + 'description': 'ID of the storage engine in which to place user-uploaded files', + 'x-example': 's3_us_east', + }, + 'USERFILES_PATH': { + 'type': 'string', + 'description': 'Path under storage in which to place user-uploaded files', + 'x-example': 'userfiles', + }, + 'ACTION_LOG_ARCHIVE_LOCATION': { + 'type': 'string', + 'description': 'If action log archiving is enabled, the storage engine in which to place the ' + + 'archived data.', + 'x-example': 's3_us_east', + }, + 'ACTION_LOG_ARCHIVE_PATH': { + 'type': 'string', + 'description': 'If action log archiving is enabled, the path in storage in which to place the ' + + 'archived data.', + 'x-example': 'archives/actionlogs', + }, + 'LOG_ARCHIVE_LOCATION': { + 'type': 'string', + 'description': 'If builds are enabled, the storage engine in which to place the ' + + 'archived build logs.', + 'x-example': 's3_us_east', + }, + 'LOG_ARCHIVE_PATH': { + 'type': 'string', + 'description': 'If builds are enabled, the path in storage in which to place the ' + + 'archived build logs.', + 'x-example': 'archives/buildlogs', + }, # Authentication. 'AUTHENTICATION_TYPE': { @@ -254,7 +401,7 @@ CONFIG_SCHEMA = { # OAuth configuration. 'GITHUB_LOGIN_CONFIG': { - 'type': 'object', + 'type': ['object', 'null'], 'description': 'Configuration for using GitHub (Enterprise) as an external login provider', 'required': ['GITHUB_ENDPOINT', 'CLIENT_ID', 'CLIENT_SECRET'], 'x-reference': 'https://coreos.com/quay-enterprise/docs/latest/github-auth.html', @@ -296,8 +443,26 @@ CONFIG_SCHEMA = { }, }, }, + 'BITBUCKET_TRIGGER_CONFIG': { + 'type': ['object', 'null'], + 'description': 'Configuration for using BitBucket for build triggers', + 'required': ['CLIENT_ID', 'CLIENT_SECRET'], + 'x-reference': 'https://coreos.com/quay-enterprise/docs/latest/bitbucket-build.html', + 'properties': { + 'CLIENT_ID': { + 'type': 'string', + 'description': 'The registered client ID for this Quay instance', + 'x-example': '0e8dbe15c4c7630b6780', + }, + 'CLIENT_SECRET': { + 'type': 'string', + 'description': 'The registered client secret for this Quay instance', + 'x-example': 'e4a58ddd3d7408b7aec109e85564a0d153d3e846', + }, + }, + }, 'GITHUB_TRIGGER_CONFIG': { - 'type': 'object', + 'type': ['object', 'null'], 'description': 'Configuration for using GitHub (Enterprise) for build triggers', 'required': ['GITHUB_ENDPOINT', 'CLIENT_ID', 'CLIENT_SECRET'], 'x-reference': 'https://coreos.com/quay-enterprise/docs/latest/github-build.html', @@ -327,7 +492,7 @@ CONFIG_SCHEMA = { }, }, 'GOOGLE_LOGIN_CONFIG': { - 'type': 'object', + 'type': ['object', 'null'], 'description': 'Configuration for using Google for external authentication', 'required': ['CLIENT_ID', 'CLIENT_SECRET'], 'properties': { @@ -344,7 +509,7 @@ CONFIG_SCHEMA = { }, }, 'GITLAB_TRIGGER_CONFIG': { - 'type': 'object', + 'type': ['object', 'null'], 'description': 'Configuration for using Gitlab (Enterprise) for external authentication', 'required': ['GITLAB_ENDPOINT', 'CLIENT_ID', 'CLIENT_SECRET'], 'properties': { @@ -366,7 +531,39 @@ CONFIG_SCHEMA = { }, }, + # Health. + 'HEALTH_CHECKER': { + 'description': 'The configured health check.', + 'x-example': ('RDSAwareHealthCheck', {'access_key': 'foo', 'secret_key': 'bar'}), + }, + + # Metrics. + 'PROMETHEUS_NAMESPACE': { + 'type': 'string', + 'description': 'The prefix applied to all exposed Prometheus metrics. Defaults to `quay`', + 'x-example': 'myregistry', + }, + # Misc configuration. + 'BLACKLIST_V2_SPEC': { + 'type': 'string', + 'description': 'The Docker CLI versions to which Quay will respond that V2 is *unsupported*. Defaults to `<1.6.0`', + 'x-reference': 'http://pythonhosted.org/semantic_version/reference.html#semantic_version.Spec', + 'x-example': '<1.8.0', + }, + 'USER_RECOVERY_TOKEN_LIFETIME': { + 'type': 'string', + 'description': 'The length of time a token for recovering a user accounts is valid. Defaults to 30m.', + 'x-example': '10m', + 'pattern': '^[0-9]+(w|m|d|h|s)$', + }, + 'SESSION_COOKIE_SECURE': { + 'type': 'boolean', + 'description': 'Whether the `secure` property should be set on session cookies. ' + + 'Defaults to False. Recommended to be True for all installations using SSL.', + 'x-example': True, + 'x-reference': 'https://en.wikipedia.org/wiki/Secure_cookies', + }, 'PUBLIC_NAMESPACES': { 'type': 'array', 'description': 'If a namespace is defined in the public namespace list, then it will appear on *all*' + @@ -386,6 +583,13 @@ CONFIG_SCHEMA = { 'V2_PAGINATION_SIZE': { 'type': 'number', 'description': 'The number of results returned per page in V2 registry APIs', + 'x-example': 100, + }, + 'ENABLE_HEALTH_DEBUG_SECRET': { + 'type': ['string', 'null'], + 'description': 'If specified, a secret that can be given to health endpoints to see full debug info when' + + 'not authenticated as a superuser', + 'x-example': 'somesecrethere', }, # Time machine and tag expiration settings. @@ -420,6 +624,11 @@ CONFIG_SCHEMA = { 'x-example': '2h', 'pattern': '^[0-9]+(w|m|d|h|s)$', }, + 'FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP': { + 'type': 'boolean', + 'description': 'If enabled, non-superusers can setup syncing on teams to backing LDAP or Keystone. Defaults To False.', + 'x-example': True, + }, # Security scanning. 'FEATURE_SECURITY_SCANNER': { @@ -498,6 +707,35 @@ CONFIG_SCHEMA = { 'x-example': False, }, + # Recaptcha + 'FEATURE_RECAPTCHA': { + 'type': 'boolean', + 'description': 'Whether Recaptcha is necessary for user login and recovery. Defaults to False', + 'x-example': False, + 'x-reference': 'https://www.google.com/recaptcha/intro/', + }, + 'RECAPTCHA_SITE_KEY': { + 'type': 'string', + 'description': 'If recaptcha is enabled, the site key for the Recaptcha service', + }, + 'RECAPTCHA_SECRET_KEY': { + 'type': 'string', + 'description': 'If recaptcha is enabled, the secret key for the Recaptcha service', + }, + + # External application tokens. + 'FEATURE_APP_SPECIFIC_TOKENS': { + 'type': 'boolean', + 'description': 'If enabled, users can create tokens for use by the Docker CLI. Defaults to True', + 'x-example': False, + }, + + 'APP_SPECIFIC_TOKEN_EXPIRATION': { + 'type': ['string', 'null'], + 'description': 'The expiration for external app tokens. Defaults to None.', + 'pattern': '^[0-9]+(w|m|d|h|s)$', + }, + # Feature Flag: Permanent Sessions. 'FEATURE_PERMANENT_SESSIONS': { 'type': 'boolean', @@ -616,6 +854,20 @@ CONFIG_SCHEMA = { 'description': 'If set to true, autocompletion will apply to partial usernames. Defaults to True', 'x-example': True, }, + + # Feature Flag: User log access. + 'FEATURE_USER_LOG_ACCESS': { + 'type': 'boolean', + 'description': 'If set to true, users will have access to audit logs for their namespace. Defaults to False', + 'x-example': True, + }, + + # Feature Flag: User renaming. + 'FEATURE_USER_RENAME': { + 'type': 'boolean', + 'description': 'If set to true, users can rename their own namespace. Defaults to False', + 'x-example': True, + }, }, } diff --git a/util/config/test/test_schema.py b/util/config/test/test_schema.py new file mode 100644 index 000000000..dba0f4354 --- /dev/null +++ b/util/config/test/test_schema.py @@ -0,0 +1,7 @@ +from config import DefaultConfig +from util.config.schema import CONFIG_SCHEMA, INTERNAL_ONLY_PROPERTIES + +def test_ensure_schema_defines_all_fields(): + for key in vars(DefaultConfig): + has_key = key in CONFIG_SCHEMA['properties'] or key in INTERNAL_ONLY_PROPERTIES + assert has_key, "Property `%s` is missing from config schema" % key