Merge remote-tracking branch 'upstream/configuration' into 170214_encryption

Conflicts:
	src/common/utils/registry/auth/tokenauthorizer.go
	src/common/utils/test/adminserver.go
	src/jobservice/replication/transfer.go
	src/ui/api/config.go
This commit is contained in:
Wenkai Yin 2017-02-20 12:21:56 +08:00
commit 385d76e6f2
14 changed files with 726 additions and 94 deletions

View File

@ -140,7 +140,7 @@ type standardTokenAuthorizer struct {
// NewStandardTokenAuthorizer returns a standard token authorizer. The authorizer will request a token
// from token server and add it to the origin request
// If tokenServiceURL is set, the token request will be sent to it instead of the server get from authorizer
// If tokenServiceEndpoint is set, the token request will be sent to it instead of the server get from authorizer
// The usage please refer to the function tokenURL
func NewStandardTokenAuthorizer(credential Credential, insecure bool,
tokenServiceEndpoint string, scopeType, scopeName string, scopeActions ...string) Authorizer {

View File

@ -23,7 +23,6 @@ import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"github.com/docker/distribution"
@ -462,15 +461,6 @@ func (m *ManifestPusher) enter() (string, error) {
func newRepositoryClient(endpoint string, insecure bool, credential auth.Credential,
tokenServiceEndpoint, repository, scopeType, scopeName string,
scopeActions ...string) (*registry.Repository, error) {
domain, err := config.ExtEndpoint()
if err != nil {
return nil, err
}
if err := os.Setenv("DOMAIN_NAME", domain); err != nil {
return nil, err
}
authorizer := auth.NewStandardTokenAuthorizer(credential, insecure,
tokenServiceEndpoint, scopeType, scopeName, scopeActions...)

View File

@ -14,7 +14,7 @@
*/
.switch-pane-admin-options {
display: inline;
width: 245px;
width: 340px;
float: right;
list-style-type: none;
}
@ -29,3 +29,7 @@
border-bottom: 2px solid rgb(0, 84, 190);
font-weight: bold;
}
.inline-help-config {
padding: 6px;
}

View File

@ -12,65 +12,224 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<form name="form" class="form-horizontal" ng-submit="form.$valid && vm.changeSettings(system)" autocomplete="off">
<div class="col-md-12">
<h5>System Settings</h5>
<hr/>
</div>
<div class="col-md-12 col-md-off-set-1 main-content">
<div class="row">
<ul id="ulTabHeader" class="nav nav-pills nav-stacked col-md-2 col-xs-12">
<li role="presentation"><a href="#auth" aria-controls="auth" role="tab" data-toggle="tab">// 'authentication' | tr //</a></li>
<li role="presentation"><a href="#email" aria-controls="email" role="tab" data-toggle="tab">// 'email_settings' | tr //</a></li>
<li role="presentation"><a href="#system" aria-controls="system" role="tab" data-toggle="tab">// 'system_settings' | tr //</a></li>
</ul>
<!-- Tab panes -->
<div id="tabConfigurations" class="tab-content col-md-10 col-xs-12">
<div role="tabpanel" class="tab-pane" id="auth">
<form name="authForm" class="form-horizontal" no-validate autocomplete="off">
<div class="form-group">
<label for="hostName" class="col-sm-3 control-label">Host Name:</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="hostName" ng-model="system.hostName" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.hostName" name="uHostName" required>
<div ng-messages="form.$dirty && form.uHostName.$error">
<span ng-message="required">Host name is required.</span>
<label for="selAuth" class="col-sm-3 control-label">// 'authentication_mode' | tr //<span ng-if="vm.warning['auth.authMode']">*</span></label>
<div class="col-sm-2">
<select id="selAuth" class="form-control" ng-model="auth.authMode.data" ng-options="r as r.name for r in vm.supportedAuths track by r.value" ng-disabled="!vm.editable['auth.authMode']"></select>
</div>
<div class="inline-help-config">
<inline-help help-title="// 'authentication_mode' | tr //" content="// 'authentication_mode_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value !== 'ldap_auth'">
<label for="selfRegistration" class="col-sm-3 control-label">// 'self_registration' | tr //<span ng-if="vm.warning['auth.selfRegistration']">*</span></label>
<div class="col-sm-5">
<select class="form-control" ng-model="auth.selfRegistration.data" ng-options="r as r.name for r in vm.toggleBooleans track by r.value" ng-disabled="!vm.editable['auth.selfRegistration']"></select>
</div>
<div class="inline-help-config">
<inline-help help-title="// 'self_registration' | tr //" content="// 'self_registration_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="ldapURL" class="col-sm-3 control-label">// 'ldap_url' | tr //<span ng-if="vm.warning['auth.ldapURL']">*</span></label>
<div class="col-sm-5">
<input id="ldapURL" type="text" class="form-control" ng-model="auth.ldapURL.data" ng-disabled="!vm.editable['auth.ldapURL']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'ldap_url' | tr //" content="// 'ldap_url_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="ldapSearchDN" class="col-sm-3 control-label">// 'ldap_search_dn' | tr //<span ng-if="vm.warning['auth.ldapSearchDN']">*</span></label>
<div class="col-sm-5 clearfix">
<input id="ldapSearchDN" type="text" class="form-control" ng-model="auth.ldapSearchDN.data" ng-disabled="!vm.editable['auth.ldapSearchDN']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'ldap_search_dn' | tr //" content="// 'ldap_search_dn_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="ldapSearchPassword" class="col-sm-3 control-label">// 'ldap_search_password' | tr //<span ng-if="vm.warning['auth.ldapSearchPassword']">*</span></label>
<div class="col-sm-5">
<input id="ldapSearchPassword" type="password" class="form-control" ng-model="auth.ldapSearchPassword.data" ng-click="vm.clearUp(auth.ldapSearchPassword)" ng-change="vm.hasChanged(auth.ldapSearchPassword)" ng-blur="vm.setMaskPassword(auth.ldapSearchPassword)">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'ldap_search_password' | tr //" content="// 'ldap_search_password_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="ldapBaseDN" class="col-sm-3 control-label">// 'ldap_base_dn' | tr //<span ng-if="vm.warning['auth.ldapBaseDN']">*</span></label>
<div class="col-sm-5">
<input id="ldapBaseDN" type="text" class="form-control" ng-model="auth.ldapBaseDN.data" ng-disabled="!vm.editable['auth.ldapBaseDN']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'ldap_base_dn' | tr //" content="// 'ldap_base_dn_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="ldapUID" class="col-sm-3 control-label">// 'ldap_uid' | tr //<span ng-if="vm.warning['auth.ldapUID']">*</span></label>
<div class="col-sm-5">
<input id="ldapUID" type="text" class="form-control" ng-model="auth.ldapUID.data" ng-disabled="!vm.editable['auth.ldapUID']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'ldap_uid' | tr //" content="// 'ldap_uid_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="ldapFilter" class="col-sm-3 control-label">// 'ldap_filter' | tr //<span ng-if="vm.warning['auth.ldapFilter']">*</span></label>
<div class="col-sm-5 clearfix">
<input id="ldapFilter" type="text" class="form-control" ng-model="auth.ldapFilter.data" ng-disabled="!vm.editable['auth.ldapFilter']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'ldap_filter' | tr //" content="// 'ldap_filter_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="ldapTimeout" class="col-sm-3 control-label">// 'ldap_connection_timeout' | tr //<span ng-if="vm.warning['auth.ldapConnectionTimeout']">*</span></label>
<div class="col-sm-2 clearfix">
<input id="ldapConnectionTimeout" type="number" class="form-control" name="ldapConnectionTimeout" ng-model="auth.ldapConnectionTimeout.data" ng-disabled="!vm.editable['auth.ldapConnectionTimeout']" min="1" max="60" required>
<div class="error-message" ng-messages="authForm.ldapConnectionTimeout.$error" >
<span ng-message="required">// 'timeout_is_required' | tr //</span>
<span ng-message="min">// 'invalid_timeout' | tr //</span>
<span ng-message="max">// 'invalid_timeout' | tr //</span>
</div>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<label for="selLDAPScope" class="col-sm-3 control-label">// 'ldap_scope' | tr //<span ng-if="vm.warning['auth.ldapScope']">*</span></label>
<div class="col-sm-4">
<select id="selLDAPScope" class="form-control" ng-model="auth.ldapScope.data" ng-options="item for item in [1, 2, 3]" ng-disabled="!vm.editable['auth.ldapScope']"></select>
</div>
<div class="inline-help-config">
<inline-help help-title="// 'ldap_scope' | tr //" content="// 'ldap_scope_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group" ng-if="auth.authMode.data.value === 'ldap_auth'">
<div class="col-sm-7 col-sm-offset-3">
<span ng-if="vm.isError" class="error-message" >// vm.pingMessage //</span>
<span ng-if="!vm.isError">// vm.pingMessage //</span>
</div>
</div>
<div class="form-group">
<label for="urlProtocol" class="col-sm-3 control-label">URL Protocol:</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="urlProtocol" ng-model="system.urlProtocol" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.urlProtocol" name="uUrlProtocol" required>
<div ng-messages="form.$dirty && form.uUrlProtocol.$error">
<span ng-message="required">Url protocol is required.</span>
</div>
</div>
</div>
<div class="form-group">
<label for="emailServer" class="col-sm-3 control-label">Email server:</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="emailServer" ng-model="system.emailServer" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.emailServer" name="uEmailServer" required>
<div ng-messages="form.$dirty && form.uEmailServer.$error">
<span ng-message="required">Email server is required.</span>
</div>
</div>
</div>
<div class="form-group">
<label for="ldapUrl" class="col-sm-3 control-label">LDAP URL:</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="ldapUrl" ng-model="system.ldapUrl" ng-model-options="{ updateOn: 'blur' }" ng-value="vm.system.ldapUrl" name="uLdapUrl" required>
<div ng-messages="form.$dirty && form.uLdapUrl.$error">
<span ng-message="required">LDAP URL is required.</span>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<h5>Registration</h5>
<hr/>
</div>
<div class="col-md-12 col-md-off-set-1 main-content">
<div class="form-group">
<label for="registration" class="col-sm-3 control-label">Registration:</label>
<div class="col-sm-7">
<select class="form-control" ng-model="vm.currentRegistration" ng-options="r as r.name for r in vm.registrationOptions track by r.value" ng-click="vm.selectRegistration()"></select>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-7 col-md-10">
<input type="submit" class="btn btn-primary" ng-disabled="form.$invalid" value="Save">
<input type="submit" class="btn btn-default" ng-click="vm.cancel()" value="Cancel">
<div class="col-sm-offset-3 col-sm-5">
<div class="pull-right">
<button type="submit" class="btn btn-link" ng-if="vm.changed" ng-click="vm.undo()">// 'undo' | tr //</button>
<button type="button" class="btn btn-default" ng-if="auth.authMode.data.value === 'ldap_auth'" ng-disabled="authForm.$invalid" ng-click="vm.pingLDAP(auth)" loading-progress hide-target="false" toggle-in-progress="vm.pingTIP">// 'test_connection' | tr //</button>
<button type="submit" class="btn btn-success" ng-disabled="authForm.$invalid" ng-click="vm.saveAuthConf(auth)">// 'save' | tr //</button>
</div>
</div>
</div>
</form>
</div>
<div role="tabpanel" class="tab-pane" id="email">
<form name="emailForm" class="form-horizontal" novalidate autocomplete="off">
<div class="form-group">
<label for="emailServer" class="col-sm-3 control-label">// 'email_server' | tr //<span ng-if="vm.warning['email.server']">*</span></label>
<div class="col-sm-5">
<input id="emailServer" type="text" class="form-control" ng-model="email.server.data" ng-disabled="!vm.editable['email.server']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'email_server' | tr //" content="// 'email_server_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<label for="emailServerPort" class="col-sm-3 control-label">// 'email_server_port' | tr //<span ng-if="vm.warning['email.serverPort']">*</span></label>
<div class="col-sm-5">
<input id="emailServerPort" type="number" class="form-control" ng-model="email.serverPort.data" name="emailServerPort" ng-disabled="!vm.editable['email.serverPort']" min="1" max="65535">
<div class="error-message" ng-messages="emailForm.emailServerPort.$error" >
<span ng-message="min">// 'invalid_port_number' | tr //</span>
<span ng-message="max">// 'invalid_port_number' | tr //</span>
</div>
</div>
<div class="inline-help-config">
<inline-help help-title="// 'email_server_port' | tr //" content="// 'email_server_port_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<label for="emailUsername" class="col-sm-3 control-label">// 'email_username' | tr //<span ng-if="vm.warning['email.username']">*</span></label>
<div class="col-sm-5">
<input id="emailServerPort" type="text" class="form-control" ng-model="email.username.data" ng-disabled="!vm.editable['email.username']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'email_username' | tr //" content="// 'email_username_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<label for="emailPassword" class="col-sm-3 control-label">// 'email_password' | tr //<span ng-if="vm.warning['email.password']">*</span></label>
<div class="col-sm-5">
<input id="emailPassword" type="password" class="form-control" ng-model="email.password.data" ng-click="vm.clearUp(email.password)" ng-change="vm.hasChanged(email.password)" ng-blur="vm.setMaskPassword(email.password)">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'email_password' | tr //" content="// 'email_password_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<label for="emailFrom" class="col-sm-3 control-label">// 'email_from' | tr //<span ng-if="vm.warning['email.from']">*</span></label>
<div class="col-sm-5">
<input id="emailFrom" type="text" class="form-control" ng-model="email.from.data" ng-disabled="!vm.editable['email.from']">
</div>
<div class="inline-help-config">
<inline-help help-title="// 'email_from' | tr //" content="// 'email_from_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<label for="emailSSL" class="col-sm-3 control-label">// 'email_ssl' | tr //<span ng-if="vm.warning['email.SSL']">*</span></label>
<div class="col-sm-5">
<select class="form-control" ng-model="email.SSL.data" ng-options="r as r.name for r in vm.toggleBooleans track by r.value" ng-disabled="!vm.editable['email.SSL']"></select>
</div>
<div class="inline-help-config">
<inline-help help-title="// 'email_ssl' | tr //" content="// 'email_ssl_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-5">
<div class="pull-right">
<button type="submit" class="btn btn-link" ng-if="vm.changed" ng-click="vm.undo()">// 'undo' | tr //</button>
<button type="submit" class="btn btn-success" ng-disabled="emailForm.$invalid" ng-click="vm.saveEmailConf(email)">// 'save' | tr //</button>
</div>
</div>
</div>
</form>
</div>
<div role="tabpanel" class="tab-pane" id="system">
<form name="systemForm" class="form-horizontal" no-validate autocomplete="off">
<div class="form-group">
<label for="projectCreationRestriction" class="col-sm-3 control-label">// 'project_creation_restriction' | tr //<span ng-if="vm.warning['system.projectCreationRestriction']">*</span></label>
<div class="col-sm-5">
<select id="projectCreationRestriction" class="form-control" ng-model="system.projectCreationRestriction.data" ng-options="r as r.name for r in vm.toggleCustoms track by r.value" ng-disabled="!vm.editable['system.projectCreationRestriction']"></select>
</div>
<div class="inline-help-config">
<inline-help help-title="// 'project_creation_restriction' | tr //" content="// 'project_creation_restriction_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<label for="verifyRemoteCert" class="col-sm-3 control-label">// 'verify_remote_cert' | tr //<span ng-if="vm.warning['system.verifyRemoteCert']">*</span></label>
<div class="col-sm-5">
<select id="verifyRemoteCert" class="form-control" ng-model="system.verifyRemoteCert.data" ng-options="r as r.name for r in vm.toggleBooleans track by r.value" ng-disabled="!vm.editable['system.verifyRemoteCert']"></select>
</div>
<div class="inline-help-config">
<inline-help help-title="// 'verify_remote_cert' | tr //" content="// 'verify_remote_cert_desc' | tr //"></inline-help>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-5">
<div class="pull-right">
<button type="submit" class="btn btn-link" ng-if="vm.changed" ng-click="vm.undo()">// 'undo' | tr //</button>
<button type="submit" class="btn btn-success" ng-disabled="systemForm.$invalid" ng-click="vm.saveSystemConf(system)">// 'save' | tr //</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>

View File

@ -18,51 +18,365 @@
angular
.module('harbor.system.management')
.constant('defaultPassword', '12345678')
.directive('configuration', configuration);
ConfigurationController.$inject = [];
ConfigurationController.$inject = ['$scope', 'ConfigurationService', 'defaultPassword', '$filter', 'trFilter'];
function ConfigurationController() {
function ConfigurationController($scope, ConfigurationService, defaultPassword, $filter, trFilter) {
var vm = this;
vm.registrationOptions = [
vm.toggleBooleans = [
{
'name': 'on',
'name': 'True',
'value': true
},
{
'name': 'off',
'name': 'False',
'value': false
}
];
vm.currentRegistration = {
'name': 'on',
'value': true
vm.toggleCustoms = [
{
'name': 'Admin Only',
'value': 'adminonly',
},
{
'name': 'Everyone',
'value': 'everyone'
}
];
vm.supportedAuths = [
{
'name': 'DB auth',
'value': 'db_auth'
},
{
'name': 'LDAP auth',
'value': 'ldap_auth'
}
];
var confKeyDefinitions = {
'auth_mode': { type: 'auth', attr: 'authMode' },
'self_registration': { type: 'auth', attr: 'selfRegistration' },
'ldap_url': { type: 'auth', attr: 'ldapURL' },
'ldap_search_dn': { type: 'auth', attr: 'ldapSearchDN' },
'ldap_search_password': { type: 'auth', attr: 'ldapSearchPassword' },
'ldap_base_dn': { type: 'auth', attr: 'ldapBaseDN' },
'ldap_uid': { type: 'auth', attr: 'ldapUID' },
'ldap_filter': { type: 'auth', attr: 'ldapFilter' },
'ldap_timeout': { type: 'auth', attr: 'ldapConnectionTimeout' },
'ldap_scope': { type: 'auth', attr: 'ldapScope' },
'email_host': { type: 'email', attr: 'server' },
'email_port': { type: 'email', attr: 'serverPort' },
'email_username': { type: 'email', attr: 'username' },
'email_password': { type: 'email', attr: 'password' },
'email_from': { type: 'email', attr: 'from' },
'email_ssl': { type: 'email', attr: 'SSL' },
'project_creation_restriction': { type: 'system', attr: 'projectCreationRestriction' },
'verify_remote_cert': { type: 'system', attr: 'verifyRemoteCert' }
};
vm.changeSettings = changeSettings;
$scope.auth = {};
$scope.email = {};
$scope.system = {};
vm.selectRegistration = selectRegistration;
vm.retrieve = retrieve;
function selectRegistration() {
vm.saveAuthConf = saveAuthConf;
vm.saveEmailConf = saveEmailConf;
vm.saveSystemConf = saveSystemConf;
vm.gatherUpdateItems = gatherUpdateItems;
vm.clearUp = clearUp;
vm.hasChanged = hasChanged;
vm.setMaskPassword = setMaskPassword;
vm.undo = undo;
vm.pingLDAP = pingLDAP;
vm.pingTIP = false;
vm.isError = false;
vm.pingMessage = '';
vm.retrieve();
function retrieve() {
vm.ldapSearchPasswordChanged = false;
vm.emailPasswordChanged = false;
vm.changedItems = {};
vm.updatedItems = {};
vm.warning = {};
vm.editable = {};
ConfigurationService
.get()
.then(getConfigurationSuccess, getConfigurationFailed);
}
function changeSettings(system) {
console.log(system);
function getConfigurationSuccess(response) {
var data = response.data || [];
for(var key in data) {
var mappedDef = keyMapping(key);
if(mappedDef) {
$scope[mappedDef['type']][mappedDef['attr']] = { 'target': mappedDef['type'] + '.' + mappedDef['attr'], 'data': valueMapping(data[key]['value']) };
$scope.$watch(mappedDef['type'] + '.' + mappedDef['attr'], onChangedCallback, true);
$scope[mappedDef['type']][mappedDef['attr']]['origin'] = { 'target': mappedDef['type'] + '.' + mappedDef['attr'], 'data': valueMapping(data[key]['value']) };
vm.editable[mappedDef['type'] + '.' + mappedDef['attr']] = data[key]['editable'];
}
}
function configuration() {
$scope.auth.ldapSearchPassword = { 'target': 'auth.ldapSearchPassword', 'data': defaultPassword};
$scope.email.password = { 'target': 'email.password', 'data': defaultPassword};
$scope.$watch('auth.ldapSearchPassword', onChangedCallback, true);
$scope.$watch('email.password', onChangedCallback, true);
$scope.auth.ldapSearchPassword.actual = { 'target': 'auth.ldapSearchPassword', 'data': ''};
$scope.email.password.actual = { 'target': 'email.password', 'data': ''};
}
function keyMapping(confKey) {
for (var key in confKeyDefinitions) {
if (confKey === key) {
return confKeyDefinitions[key];
}
}
return null;
}
function valueMapping(value) {
switch(value) {
case true:
return vm.toggleBooleans[0];
case false:
return vm.toggleBooleans[1];
case 'db_auth':
return vm.supportedAuths[0];
case 'ldap_auth':
return vm.supportedAuths[1];
case 'adminonly':
return vm.toggleCustoms[0];
case 'everyone':
return vm.toggleCustoms[1];
default:
return value;
}
}
function onChangedCallback(current, previous) {
if(!angular.equals(current, previous)) {
var compositeKey = current.target.split(".");
vm.changed = false;
var changedData = {};
switch(current.target) {
case 'auth.ldapSearchPassword':
if(vm.ldapSearchPasswordChanged) {
vm.changed = true;
changedData = $scope.auth.ldapSearchPassword.actual.data;
}
break;
case 'email.password':
if(vm.emailPasswordChanged) {
vm.changed = true;
changedData = $scope.email.password.actual.data;
}
break;
default:
if(!angular.equals(current.data, $scope[compositeKey[0]][compositeKey[1]]['origin']['data'])) {
vm.changed = true;
changedData = current.data;
}
}
if(vm.changed) {
vm.changedItems[current.target] = changedData;
vm.warning[current.target] = true;
} else {
delete vm.changedItems[current.target];
vm.warning[current.target] = false;
}
}
}
function getConfigurationFailed(response) {
console.log('Failed to get configurations.');
}
function updateConfigurationSuccess(response) {
$scope.$emit('modalTitle', $filter('tr')('update_configuration_title', []));
$scope.$emit('modalMessage', $filter('tr')('successful_update_configuration', []));
var emitInfo = {
'confirmOnly': true,
'contentType': 'text/plain',
'action' : function() {
vm.retrieve();
}
};
$scope.$emit('raiseInfo', emitInfo);
console.log('Updated system configuration successfully.');
}
function updateConfigurationFailed() {
$scope.$emit('modalTitle', $filter('tr')('update_configuration_title', []));
$scope.$emit('modalMessage', $filter('tr')('failed_to_update_configuration', []));
$scope.$emit('raiseError', true);
console.log('Failed to update system configurations.');
}
function gatherUpdateItems() {
vm.updatedItems = {};
for(var key in confKeyDefinitions) {
var value = confKeyDefinitions[key];
var compositeKey = value.type + '.' + value.attr;
for(var itemKey in vm.changedItems) {
var item = vm.changedItems[itemKey];
if (compositeKey === itemKey) {
(typeof item === 'object' && item) ? vm.updatedItems[key] = ((typeof item.value === 'boolean') ? Number(item.value) + '' : item.value) : vm.updatedItems[key] = String(item) || '';
}
}
}
}
function saveAuthConf(auth) {
vm.gatherUpdateItems();
console.log('auth changed:' + angular.toJson(vm.updatedItems));
ConfigurationService
.update(vm.updatedItems)
.then(updateConfigurationSuccess, updateConfigurationFailed);
}
function saveEmailConf(email) {
vm.gatherUpdateItems();
console.log('email changed:' + angular.toJson(vm.updatedItems));
ConfigurationService
.update(vm.updatedItems)
.then(updateConfigurationSuccess, updateConfigurationFailed);
}
function saveSystemConf(system) {
vm.gatherUpdateItems();
console.log('system changed:' + angular.toJson(vm.updatedItems));
ConfigurationService
.update(vm.updatedItems)
.then(updateConfigurationSuccess, updateConfigurationFailed);
}
function clearUp(input) {
switch(input.target) {
case 'auth.ldapSearchPassword':
$scope.auth.ldapSearchPassword.data = '';
break;
case 'email.password':
$scope.email.password.data = '';
break;
}
}
function hasChanged(input) {
switch(input.target) {
case 'auth.ldapSearchPassword':
vm.ldapSearchPasswordChanged = true;
$scope.auth.ldapSearchPassword.actual.data = input.data;
break;
case 'email.password':
vm.emailPasswordChanged = true;
$scope.email.password.actual.data = input.data;
break;
}
}
function setMaskPassword(input) {
input.data = defaultPassword;
}
function undo() {
vm.retrieve();
}
function pingLDAP(auth) {
var keyset = [
{'name': 'ldapURL' , 'attr': 'ldap_url'},
{'name': 'ldapSearchDN', 'attr': 'ldap_search_dn'},
{'name': 'ldapSearchPassword' , 'attr': 'ldap_search_password'},
{'name': 'ldapConnectionTimeout', 'attr': 'ldap_connection_timeout'}
];
var ldapConf = {};
for(var i = 0; i < keyset.length; i++) {
var key = keyset[i];
var value;
if(key.name === 'ldapSearchPassword') {
value = auth[key.name]['actual']['data'];
}else {
value = auth[key.name]['data'];
}
ldapConf[key.attr] = value;
}
vm.pingMessage = $filter('tr')('pinging_target');
vm.pingTIP = true;
vm.isError = false;
ConfigurationService
.pingLDAP(ldapConf)
.then(pingLDAPSuccess, pingLDAPFailed);
}
function pingLDAPSuccess(response) {
vm.pingTIP = false;
vm.pingMessage = $filter('tr')('successful_ping_target');
}
function pingLDAPFailed(response) {
vm.isError = true;
vm.pingTIP = false;
vm.pingMessage = $filter('tr')('failed_to_ping_target');
console.log('Failed to ping LDAP target:' + response.data);
}
}
configuration.$inject = ['$filter', 'trFilter'];
function configuration($filter, trFilter) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/components/system-management/configuration.directive.html',
'scope': true,
'link': link,
'controller': ConfigurationController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
element.find('#ulTabHeader a').on('click', function(e) {
e.preventDefault();
ctrl.gatherUpdateItems();
if(!angular.equals(ctrl.updatedItems, {})) {
var emitInfo = {
'confirmOnly': true,
'contentType': 'text/html',
'action' : function() {
return;
}
};
scope.$emit('modalTitle', $filter('tr')('caution'));
scope.$emit('modalMessage', $filter('tr')('please_save_changes'));
scope.$emit('raiseInfo', emitInfo);
scope.$apply();
e.stopPropagation();
}else{
$(this).tab('show');
}
});
element.find('#ulTabHeader a:first').trigger('click');
}
}
})();

View File

@ -29,6 +29,7 @@
switch(currentTarget) {
case 'destinations':
case 'replication':
case 'configuration':
$location.path('/' + currentTarget);
vm.target = currentTarget;
break;

View File

@ -12,10 +12,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<ul class="switch-pane-admin-options" role="tablist">
<ul class="switch-pane-admin-options" role="tablist" ng-style="vm.customPos">
<li><a tag="destinations" href="#/destinations">// 'destination' | tr //</a><span class="gutter">|</span></li>
<li><a tag="replication" href="#/replication">// 'replication' | tr //</a><span class="gutter">
<!--
<li><a tag="configuration" href="#/configuration">Configuration</a></li>
-->
<li><a tag="replication" href="#/replication">// 'replication' | tr //</a><span class="gutter">|</span>
<li><a tag="configuration" href="#/configuration">// 'configuration' | tr //</a></li>
</ul>

View File

@ -27,7 +27,9 @@
vm.path = $location.path();
}
function navigationAdminOptions() {
navigationAdminOptions.$inject = ['I18nService'];
function navigationAdminOptions(I18nService) {
var directive = {
'restrict': 'E',
'templateUrl': '/static/resources/js/layout/navigation/navigation-admin-options.directive.html',
@ -45,6 +47,13 @@
var visited = ctrl.path.substring(1);
console.log('visited:' + visited);
var lang = I18nService().getCurrentLanguage();
ctrl.customPos = {};
if(lang === 'zh-CN') {
ctrl.customPos = {'position': 'relative', 'left': '14%'};
}
if(visited) {
element.find('a[tag="' + visited + '"]').addClass('active');
}else{

View File

@ -35,7 +35,9 @@
vm.path = $location.path();
}
function navigationDetails() {
navigationDetails.$inject = ['I18nService'];
function navigationDetails(I18nService) {
var directive = {
restrict: 'E',
templateUrl: '/navigation_detail?timestamp=' + new Date().getTime(),
@ -53,6 +55,13 @@
function link(scope, element, attrs, ctrl) {
var lang = I18nService().getCurrentLanguage();
ctrl.customPos = {};
if(lang === 'zh-CN') {
ctrl.customPos = {'position': 'relative', 'left': '8%'};
}
var visited = ctrl.path.substring(1);
if(visited) {

View File

@ -287,5 +287,57 @@ var locale_messages = {
'confirm_to_toggle_enabled_policy': 'After enabling the replication policy, all repositories under the project will be replicated to the destination registry. Please confirm to continue.',
'confirm_to_toggle_disabled_policy_title': 'Disable Policy',
'confirm_to_toggle_disabled_policy': 'After disabling the policy, all unfinished replication jobs of this policy will be stopped and canceled. Please confirm to continue.',
'begin_date_is_later_than_end_date': 'Begin date should not be later than end date.'
'begin_date_is_later_than_end_date': 'Begin date should not be later than end date.',
'configuration': 'Configuration',
'authentication': 'Authentication',
'email_settings': 'Email Settings',
'system_settings': 'System Settings',
'authentication_mode': 'Authentication Mode',
'authentication_mode_desc': 'The default authentication mode is db_auth. Set it to ldap_auth when users\' credentials are stored in an LDAP or AD server. Note: this option can only be set once.',
'self_registration': 'Self Registration',
'self_registration_desc': 'Determine whether the self-registration is allowed or not. Set this to off to disable a user\'s self-registration in Harbor. This flag has no effect when users are stored in LDAP or AD.',
'ldap_url': 'LDAP URL',
'ldap_url_desc': 'The URL of an LDAP/AD server.',
'ldap_search_dn': 'LDAP Search DN',
'ldap_search_dn_desc': 'A user\'s DN who has the permission to search the LDAP/AD server. Leave blank if your LDAP/AD server supports anonymous search, otherwise you should configure this DN and LDAP Search Password.',
'ldap_search_password': 'LDAP Search Password',
'ldap_search_password_desc': 'The password of the user for LDAP search. Leave blank if your LDAP/AD server supports anonymous search.',
'ldap_base_dn': 'LDAP Base DN',
'ldap_base_dn_desc': 'The base DN of a node from which to look up a user for authentication. The search scope includes subtree of the node.',
'ldap_uid': 'LDAP UID',
'ldap_uid_desc': 'The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD server.',
'ldap_filter': 'LDAP Filter',
'ldap_filter_desc': 'Search filter for LDAP/AD, make sure the syntax of the filter is correct.',
'ldap_connection_timeout': 'LDAP Connection Timeout(sec.)',
'ldap_scope': 'LDAP Scope',
'ldap_scope_desc': 'The scope to search for users.(1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE)',
'email_server': 'Email Server',
'email_server_desc': 'The mail server to send out emails to reset password.',
'email_server_port': 'Email Server Port',
'email_server_port_desc': 'The port of mail server.',
'email_username': 'Email Username',
'email_username_desc': 'The user from whom the password reset email is sent. Usually this is a system email address.',
'email_password': 'Email Password',
'email_password_desc': 'The password of the user from whom the password reset email is sent.',
'email_from': 'Email From',
'email_from_desc': 'The name of the email sender.',
'email_ssl': 'Email SSL',
'email_ssl_desc': 'Whether to enable secure mail transmission.',
'project_creation_restriction': 'Project Creation Restriction',
'project_creation_restriction_desc': 'The flag to control what users have permission to create projects. Be default everyone can create a project, set to "adminonly" such that only admin can create project.',
'verify_remote_cert': 'Verify Remote Cert.',
'verify_remote_cert_desc': 'Determine whether the image replication should verify the certificate of a remote Harbor registry. Set this flag to off when the remote registry uses a self-signed or untrusted certificate.',
'max_job_workers': 'Max Job Workers',
'max_job_workers_desc': 'Maximum number of job workers in job service.',
'please_save_changes': 'Please save changes before leaving this page.',
'undo': 'Undo',
'invalid_port_number': 'Invalid port number.',
'max_job_workers_is_required': 'Max job workers number is required.',
'timeout_is_required': 'Timeout value is required.',
'invalid_timeout': 'Invalid timeout value.',
'ldap_scope_is_required': 'Scope is required.',
'invalid_ldap_scope': 'Invalid Scope value.',
'update_configuration_title': 'Update Configuration(s)',
'successful_update_configuration': 'Configuration(s) updated successfully.',
'failed_to_update_configuration': 'Failed to update configuration.'
};

View File

@ -287,5 +287,57 @@ var locale_messages = {
'confirm_to_toggle_enabled_policy': '启用策略后,该项目下的所有镜像仓库将复制到目标实例。请确认继续。',
'confirm_to_toggle_disabled_policy_title': '停用策略',
'confirm_to_toggle_disabled_policy': '停用策略后,所有未完成的复制任务将被终止和取消。请确认继续。',
'begin_date_is_later_than_end_date': '起始日期不能晚于结束日期。'
'begin_date_is_later_than_end_date': '起始日期不能晚于结束日期。',
'configuration': '设置',
'authentication': '认证设置',
'email_settings': '邮箱设置',
'system_settings': '系统设置',
'authentication_mode': '认证模式',
'authentication_mode_desc': '默认的认证模式是: 数据库认证。 当用户信息存储在LDAP或AD服务器时应设置为LDAP认证模式。 注意:该选项只能被设置一次。',
'self_registration': '自注册',
'self_registration_desc': '确定是否允许或禁止用户自注册。 关闭该项会禁用Harbor用户注册。 当用户数据存储在LDAP或AD时该选项无效。 ',
'ldap_url': 'LDAP URL',
'ldap_url_desc': 'LDAP/AD服务URL。',
'ldap_search_dn': 'LDAP Search DN',
'ldap_search_dn_desc': '提供一个拥有检索LDAP/AD权限的用户DN。 如果LDAP/AD允许匿名访问该项可以置空 否则需提供用户DN和密码。',
'ldap_search_password': 'LDAP Search 密码',
'ldap_search_password_desc': '检索LDAP的用户密码。 如果LDAP/AD允许匿名访问该项可以置空。',
'ldap_base_dn': 'LDAP Base DN',
'ldap_base_dn_desc': '用于查询认证用户的基本DN节点。 检索范围包含子树节点。',
'ldap_uid': 'LDAP UID',
'ldap_uid_desc': '该属性用于检索匹配用户, 可以是uid cn, Email, sAMAccountName或是其他属性 取决于LDAP/AD服务。',
'ldap_filter': 'LDAP 过滤器',
'ldap_filter_desc': '用于过滤LDAP/AP检索 请确保正确语法形式。',
'ldap_connection_timeout': 'LDAP连接超时(秒)',
'ldap_scope': 'LDAP检索范围',
'ldap_scope_desc': '指定用户检索范围。(1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE)',
'email_server': '邮箱服务地址',
'email_server_desc': '用以发送重置密码的邮箱服务地址。',
'email_server_port': '邮箱服务端口号',
'email_server_port_desc': '邮箱服务端口号',
'email_username': '邮箱用户名',
'email_username_desc': '发送重置密码邮箱的用户。 通常是系统邮箱地址。',
'email_password': '邮箱密码',
'email_password_desc': '发送重置密码邮箱的密码。',
'email_from': '寄信人',
'email_from_desc': '邮箱寄信人名。',
'email_ssl': '邮箱SSL',
'email_ssl_desc': '是否启用安全邮箱传输。',
'project_creation_restriction': '项目创建约束',
'project_creation_restriction_desc': '此标志用于控制用户是否有权限创建项目。 默认允许任何人创建项目, 当设置为"adminonly"只允许管理员创建项目。',
'verify_remote_cert': '验证远程服务证书',
'verify_remote_cert_desc': '确定镜像复制是否检查远程Harbor服务证书。 当使用非受信或自签名证书时应该关闭该检查。',
'max_job_workers': '最大任务调度数',
'max_job_workers_desc': '任务调度服务最大调度数。',
'please_save_changes': '请在离开此页之前保存。',
'undo': '撤销',
'invalid_port_number': '无效的端口号。',
'max_job_workers_is_required': '最大任务数为必填项。',
'timeout_is_required': '超时时间为必填项。',
'invalid_timeout': '无效的超时时间。',
'ldap_scope_is_required': '范围值为必填项。',
'invalid_ldap_scope': '无效的范围值。',
'update_configuration_title': '修改设置',
'successful_update_configuration': '修改设置成功。',
'failed_to_update_configuration': '修改设置失败。'
};

View File

@ -0,0 +1,43 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
'use strict';
angular.module('harbor.services.system.info')
.service('ConfigurationService', ConfigurationService);
ConfigurationService.$inject = ['$http', '$q', '$timeout'];
function ConfigurationService($http, $q, $timeout) {
this.get = get;
this.update = update;
this.pingLDAP = pingLDAP;
function get() {
return $http.get('/api/configurations');
}
function update(updates) {
return $http.put('/api/configurations', updates);
}
function pingLDAP(ldapConf) {
return $http
.post('/api/ldap/ping', ldapConf);
}
}
})();

View File

@ -12,7 +12,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<ul class="switch-pane-tabs pull-right" {{ if eq .IsAdmin 1 }} style="width: 365px" {{ end }} role="tablist">
<ul class="switch-pane-tabs pull-right" {{ if eq .IsAdmin 1 }} style="width: 365px" ng-style="vm.customPos" {{ end }} role="tablist">
<li><a tag="repositories" href="#/repositories?project_id=//vm.projectId//">// 'repositories' | tr //</a><span class="gutter">|</span></li>
{{ if eq .IsAdmin 1 }}
<li><a tag="replication" href="#/replication?project_id=//vm.projectId//">// 'replication' | tr //</a><span class="gutter">|</span></li>

View File

@ -112,6 +112,7 @@
<script src="/static/resources/js/services/system-info/services.system-info.module.js"></script>
<script src="/static/resources/js/services/system-info/services.volume-info.js"></script>
<script src="/static/resources/js/services/system-info/services.system-configuration.js"></script>
<script src="/static/resources/js/session/session.module.js"></script>
<script src="/static/resources/js/session/session.current-user.js"></script>