diff --git a/static/directives/config/config-setup-tool.html b/static/directives/config/config-setup-tool.html
index 205142beb..873b5650c 100644
--- a/static/directives/config/config-setup-tool.html
+++ b/static/directives/config/config-setup-tool.html
@@ -53,59 +53,6 @@
-
- Anonymous Access: |
-
-
- Enable Anonymous Access
-
-
- If enabled, public repositories and search can be accessed by anyone that can
- reach the registry, even if they are not authenticated. Disable to only allow
- authenticated users to view and pull "public" resources.
-
- |
-
-
- User Creation: |
-
-
- Enable Open User Creation
-
-
- If enabled, user accounts can be created by anyone.
- Users can always be created in the users panel under this superuser view.
-
- |
-
-
- Encrypted Client Password: |
-
-
- Require Encrypted Client Passwords
-
-
- If enabled, users will not be able to login from the Docker command
- line with a non-encrypted password and must generate an encrypted
- password to use.
-
-
- This feature is highly recommended for setups with external authentication, as Docker currently stores passwords in plaintext on user's machines.
-
- |
-
-
- Team Invitations: |
-
-
- Require Team Invitations
-
-
- If enabled, when adding a new user to a team, they will receive an invitation to join the team, with the option to decline.
- Otherwise, users will be immediately part of a team when added by a team administrator.
-
- |
-
@@ -1149,6 +1096,95 @@
+
+
+
+ Access Settings
+
+
+
+
Various settings around access and authentication to the registry.
+
+
+
+
+ Basic Credentials Login: |
+
+
+ Login to User Interface via credentials
+
+
+
+
+ Login to User Interface via credentials is enabled (requires at least one OIDC provider to disable)
+
+
+
+ If enabled, users will be able to login to the user interface via their username and password credentials.
+
+
+ If disabled, users will only be able to login to the user interface via one of the configured External Authentication providers.
+
+ |
+
+
+ Anonymous Access: |
+
+
+ Enable Anonymous Access
+
+
+ If enabled, public repositories and search can be accessed by anyone that can
+ reach the registry, even if they are not authenticated. Disable to only allow
+ authenticated users to view and pull "public" resources.
+
+ |
+
+
+ User Creation: |
+
+
+ Enable Open User Creation
+
+
+ If enabled, user accounts can be created by anyone.
+ Users can always be created in the users panel under this superuser view.
+
+ |
+
+
+ Encrypted Client Password: |
+
+
+ Require Encrypted Client Passwords
+
+
+ If enabled, users will not be able to login from the Docker command
+ line with a non-encrypted password and must generate an encrypted
+ password to use.
+
+
+ This feature is highly recommended for setups with external authentication, as Docker currently stores passwords in plaintext on user's machines.
+
+ |
+
+
+ Team Invitations: |
+
+
+ Require Team Invitations
+
+
+ If enabled, when adding a new user to a team, they will receive an invitation to join the team, with the option to decline.
+ Otherwise, users will be immediately part of a team when added by a team administrator.
+
+ |
+
+
+
+
diff --git a/static/js/core-config-setup.js b/static/js/core-config-setup.js
index d483e3d3e..763330ff6 100644
--- a/static/js/core-config-setup.js
+++ b/static/js/core-config-setup.js
@@ -23,6 +23,8 @@ angular.module("core-config-setup", ['angularFileUpload'])
{'id': 'time-machine', 'title': 'Time Machine'},
+ {'id': 'access', 'title': 'Access Settings'},
+
{'id': 'ssl', 'title': 'SSL certificate and key', 'condition': function(config) {
return config.PREFERRED_URL_SCHEME == 'https';
}},
@@ -136,6 +138,10 @@ angular.module("core-config-setup", ['angularFileUpload'])
]
};
+ $scope.enableFeature = function(config, feature) {
+ config[feature] = true;
+ };
+
$scope.validateHostname = function(hostname) {
if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) {
return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.'
diff --git a/util/config/configutil.py b/util/config/configutil.py
index ee39feba3..44ea58574 100644
--- a/util/config/configutil.py
+++ b/util/config/configutil.py
@@ -19,6 +19,7 @@ def add_enterprise_config_defaults(config_obj, current_secret_key, hostname):
config_obj['FEATURE_REQUIRE_TEAM_INVITE'] = config_obj.get('FEATURE_REQUIRE_TEAM_INVITE', True)
config_obj['FEATURE_CHANGE_TAG_EXPIRATION'] = config_obj.get('FEATURE_CHANGE_TAG_EXPIRATION',
True)
+ config_obj['FEATURE_DIRECT_LOGIN'] = config_obj.get('FEATURE_DIRECT_LOGIN', True)
# Default features that are off.
config_obj['FEATURE_MAILING'] = config_obj.get('FEATURE_MAILING', False)
diff --git a/util/config/validator.py b/util/config/validator.py
index 360942b16..4190caa34 100644
--- a/util/config/validator.py
+++ b/util/config/validator.py
@@ -20,6 +20,7 @@ from util.config.validators.validate_gitlab_trigger import GitLabTriggerValidato
from util.config.validators.validate_github import GitHubLoginValidator, GitHubTriggerValidator
from util.config.validators.validate_oidc import OIDCLoginValidator
from util.config.validators.validate_timemachine import TimeMachineValidator
+from util.config.validators.validate_access import AccessSettingsValidator
logger = logging.getLogger(__name__)
@@ -55,6 +56,7 @@ VALIDATORS = {
BittorrentValidator.name: BittorrentValidator.validate,
OIDCLoginValidator.name: OIDCLoginValidator.validate,
TimeMachineValidator.name: TimeMachineValidator.validate,
+ AccessSettingsValidator.name: AccessSettingsValidator.validate,
}
def validate_service_for_config(service, config, password=None):
diff --git a/util/config/validators/test/test_validate_access.py b/util/config/validators/test/test_validate_access.py
new file mode 100644
index 000000000..9008767eb
--- /dev/null
+++ b/util/config/validators/test/test_validate_access.py
@@ -0,0 +1,22 @@
+import pytest
+
+from util.config.validators import ConfigValidationException
+from util.config.validators.validate_access import AccessSettingsValidator
+
+from test.fixtures import *
+
+@pytest.mark.parametrize('unvalidated_config, expected_exception', [
+ ({}, None),
+ ({'FEATURE_DIRECT_LOGIN': False}, ConfigValidationException),
+ ({'FEATURE_DIRECT_LOGIN': False, 'SOMETHING_LOGIN_CONFIG': {}}, None),
+ ({'FEATURE_DIRECT_LOGIN': False, 'FEATURE_GITHUB_LOGIN': True}, None),
+ ({'FEATURE_DIRECT_LOGIN': False, 'FEATURE_GOOGLE_LOGIN': True}, None),
+])
+def test_validate_invalid_oidc_login_config(unvalidated_config, expected_exception, app):
+ validator = AccessSettingsValidator()
+
+ if expected_exception is not None:
+ with pytest.raises(expected_exception):
+ validator.validate(unvalidated_config, None, None)
+ else:
+ validator.validate(unvalidated_config, None, None)
diff --git a/util/config/validators/validate_access.py b/util/config/validators/validate_access.py
new file mode 100644
index 000000000..eb80090d8
--- /dev/null
+++ b/util/config/validators/validate_access.py
@@ -0,0 +1,22 @@
+from app import app
+from util.config.validators import BaseValidator, ConfigValidationException
+from oauth.loginmanager import OAuthLoginManager
+from oauth.oidc import OIDCLoginService
+
+class AccessSettingsValidator(BaseValidator):
+ name = "access"
+
+ @classmethod
+ def validate(cls, config, user, user_password):
+ if not config.get('FEATURE_DIRECT_LOGIN', True):
+ # Make sure we have at least one OIDC enabled.
+ github_login = config.get('FEATURE_GITHUB_LOGIN', False)
+ google_login = config.get('FEATURE_GOOGLE_LOGIN', False)
+
+ client = app.config['HTTPCLIENT']
+ login_manager = OAuthLoginManager(config, client=client)
+ custom_oidc = [s for s in login_manager.services if isinstance(s, OIDCLoginService)]
+
+ if not github_login and not google_login and not custom_oidc:
+ msg = 'Cannot disable credentials login to UI without configured OIDC service'
+ raise ConfigValidationException(msg)