Enable toggling of the direct login feature in the superuser panel
Allows superusers to disable login to the UI via credentials if at least one OIDC provider is configured
This commit is contained in:
parent
8e8470890a
commit
2b9873483a
6 changed files with 142 additions and 53 deletions
|
@ -53,59 +53,6 @@
|
|||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="non-input">Anonymous Access:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_ANONYMOUS_ACCESS">
|
||||
Enable Anonymous Access
|
||||
</div>
|
||||
<div class="help-text">
|
||||
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.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="non-input">User Creation:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_USER_CREATION">
|
||||
Enable Open User Creation
|
||||
</div>
|
||||
<div class="help-text">
|
||||
If enabled, user accounts can be created by anyone.
|
||||
Users can always be created in the users panel under this superuser view.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="non-input">Encrypted Client Password:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
|
||||
Require Encrypted Client Passwords
|
||||
</div>
|
||||
<div class="help-text">
|
||||
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.
|
||||
</div>
|
||||
<div class="help-text" ng-if="config.AUTHENTICATION_TYPE != 'Database'">
|
||||
This feature is <strong>highly recommended</strong> for setups with external authentication, as Docker currently stores passwords in <strong>plaintext</strong> on user's machines.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="config.FEATURE_MAILING">
|
||||
<td class="non-input">Team Invitations:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_REQUIRE_TEAM_INVITE">
|
||||
Require Team Invitations
|
||||
</div>
|
||||
<div class="help-text">
|
||||
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.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1149,6 +1096,95 @@
|
|||
</div>
|
||||
</div> <!-- /External Authentication -->
|
||||
|
||||
<!-- Access settings -->
|
||||
<div class="co-panel">
|
||||
<div class="co-panel-heading">
|
||||
<i class="fa fa-user-circle"></i> Access Settings
|
||||
</div>
|
||||
<div class="co-panel-body">
|
||||
<div class="description">
|
||||
<p>Various settings around access and authentication to the registry.</p>
|
||||
</div>
|
||||
|
||||
<table class="config-table">
|
||||
<tr>
|
||||
<td class="non-input">Basic Credentials Login:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_DIRECT_LOGIN" ng-if="getOIDCProviders(config).length || config.FEATURE_GITHUB_LOGIN || config.FEATURE_GOOGLE_LOGIN">
|
||||
Login to User Interface via credentials
|
||||
</div>
|
||||
<div ng-if="!getOIDCProviders(config).length && !config.FEATURE_GITHUB_LOGIN && !config.FEATURE_GOOGLE_LOGIN">
|
||||
<div ng-if="!config.FEATURE_DIRECT_LOGIN" class="co-alert co-alert-danger">
|
||||
Login to User Interface via credentials must be enabled. <a ng-click="enableFeature(config, 'FEATURE_DIRECT_LOGIN')">Click here to enable</a>.
|
||||
</div>
|
||||
<div ng-if="config.FEATURE_DIRECT_LOGIN">
|
||||
Login to User Interface via credentials is <strong>enabled</strong> (requires at least one OIDC provider to disable)
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-text">
|
||||
If enabled, users will be able to login to the <strong>user interface</strong> via their username and password credentials.
|
||||
</div>
|
||||
<div class="help-text">
|
||||
If <strong>disabled</strong>, users will only be able to login to the <strong>user interface</strong> via one of the configured External Authentication providers.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="non-input">Anonymous Access:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_ANONYMOUS_ACCESS">
|
||||
Enable Anonymous Access
|
||||
</div>
|
||||
<div class="help-text">
|
||||
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.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="non-input">User Creation:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_USER_CREATION">
|
||||
Enable Open User Creation
|
||||
</div>
|
||||
<div class="help-text">
|
||||
If enabled, user accounts can be created by anyone.
|
||||
Users can always be created in the users panel under this superuser view.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="non-input">Encrypted Client Password:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
|
||||
Require Encrypted Client Passwords
|
||||
</div>
|
||||
<div class="help-text">
|
||||
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.
|
||||
</div>
|
||||
<div class="help-text" ng-if="config.AUTHENTICATION_TYPE != 'Database'">
|
||||
This feature is <strong>highly recommended</strong> for setups with external authentication, as Docker currently stores passwords in <strong>plaintext</strong> on user's machines.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="config.FEATURE_MAILING">
|
||||
<td class="non-input">Team Invitations:</td>
|
||||
<td colspan="2">
|
||||
<div class="config-bool-field" binding="config.FEATURE_REQUIRE_TEAM_INVITE">
|
||||
Require Team Invitations
|
||||
</div>
|
||||
<div class="help-text">
|
||||
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.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div> <!-- /Access settings -->
|
||||
|
||||
<!-- Build Support -->
|
||||
<div class="co-panel">
|
||||
|
|
|
@ -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.'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
22
util/config/validators/test/test_validate_access.py
Normal file
22
util/config/validators/test/test_validate_access.py
Normal file
|
@ -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)
|
22
util/config/validators/validate_access.py
Normal file
22
util/config/validators/validate_access.py
Normal file
|
@ -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)
|
Reference in a new issue