feat(ce): fix small issues for Highlight Business Edition feature (#6043)

* feat(logs): added orange border to export button.

* fix(ldap): fixed connectivity check radio button alignment issue

* fix(be-feature): added box shadow to limited feature border

* fix(be-feature): fixed hide internal auth toggle issue.

* feat(ldap): added isLimitedFeatureSelfContained config to ldap-custom-admin-group and test-login components

* feat(auth): moved save settings button in auth page to a component.

* feat(oauth): use saveSettingsButton component in oauth

* feat(oauth): use saveSettingsButton component in internal auth

* feat(oauth): use saveSettingsButton component in MS active directory auth

* feat(oauth): use saveSettingsButton component in ldap auth

* feat(auth): added new component to index.js

* removed css inline styles.

* feat(ad): added disable check method to ad auth.

* feat(ldap): moved save settings disable method to parent component

* removed inline styles.

* fix(ldap): fixed test login misalignment issue

* fix(ldap): pass isLdapFormValid function from page component

* fix(ldap): made the toggle button in custom admin group orange when it's limited to CE.

* fix(auth): fixed save setting button in Microsoft Active Directory auth

* fix(ldap): made the assign admin toggle bright orange
pull/6167/head
fhanportainer 2021-11-29 10:41:21 +13:00 committed by GitHub
parent 18c323185e
commit 3f31d4b00b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 328 additions and 97 deletions

View File

@ -845,6 +845,54 @@ json-tree .branch-preview {
margin-bottom: 2rem;
}
.m-l-5 {
margin-left: 5px;
}
.m-l-20 {
margin-left: 20px;
}
.m-l-30 {
margin-left: 30px;
}
.m-r-2 {
margin-right: 2px;
}
.m-r-5 {
margin-right: 5px;
}
.m-t-10 {
margin-top: 10px;
}
.m-t-10 {
margin-top: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.no-margin {
margin: 0 !important;
}
.no-border {
border: none;
}
.dispay-flex {
display: flex;
}
.flex-wrap {
flex-wrap: wrap;
}
.text-wrap {
word-break: break-all;
white-space: normal;

View File

@ -6,15 +6,22 @@
border-color: var(--border-form-control-color);
}
button.limited-be {
button.limited-be,
button[disabled].limited-be.oauth-save-settings-button {
background-color: var(--BE-only);
border-color: var(--BE-only);
}
button.limited-be.oauth-save-settings-button {
background-color: var(--blue-2);
border-color: transparent;
}
ng-form.limited-be,
form.limited-be,
div.limited-be {
border: solid 1px var(--BE-only);
box-shadow: var(--shadow-boxselector-color);
padding: 10px;
pointer-events: none;
touch-action: none;

View File

@ -6,6 +6,8 @@ angular.module('portainer.oauth').component('oauthSettings', {
bindings: {
settings: '=',
teams: '<',
onSaveSettings: '<',
saveButtonState: '<',
},
controller,
});

View File

@ -8,6 +8,7 @@ export default class OAuthSettingsController {
this.featureService = featureService;
this.limitedFeature = HIDE_INTERNAL_AUTH;
this.limitedFeatureClass = 'limited-be';
this.state = {
provider: 'custom',
@ -61,7 +62,7 @@ export default class OAuthSettingsController {
}
updateSSO() {
this.settings.HideInternalAuth = this.settings.SSO;
this.settings.HideInternalAuth = this.featureService.isLimitedToBE(this.limitedFeature) ? false : this.settings.SSO;
}
addTeamMembershipMapping() {
@ -72,6 +73,20 @@ export default class OAuthSettingsController {
this.settings.TeamMemberships.OAuthClaimMappings.splice(index, 1);
}
isOAuthTeamMembershipFormValid() {
if (this.settings.OAuthAutoMapTeamMemberships && this.settings.TeamMemberships) {
if (!this.settings.TeamMemberships.OAuthClaimName) {
return false;
}
const hasInvalidMapping = this.settings.TeamMemberships.OAuthClaimMappings.some((m) => !(m.ClaimValRegex && m.Team));
if (hasInvalidMapping) {
return false;
}
}
return true;
}
$onInit() {
this.isLimitedToBE = this.featureService.isLimitedToBE(this.limitedFeature);

View File

@ -1,4 +1,4 @@
<ng-form name="$ctrl.oauthSettingsForm">
<ng-form name="oauthSettingsForm">
<div class="col-sm-12 form-section-title">
Single Sign-On
</div>
@ -379,4 +379,12 @@
</a>
</div>
</div>
<save-auth-settings-button
on-save-settings="($ctrl.onSaveSettings)"
save-button-state="($ctrl.saveButtonState)"
save-button-disabled="!$ctrl.isOAuthTeamMembershipFormValid() || oauthSettingsForm.$invalid"
limited-feature-id="$ctrl.limitedFeature"
limited-feature-class="$ctrl.limitedFeatureClass"
class-name="'oauth-save-settings-button'"
></save-auth-settings-button>
</ng-form>

View File

@ -1,10 +1,11 @@
import angular from 'angular';
import ldapModule from './ldap';
import { autoUserProvisionToggle } from './auto-user-provision-toggle';
import { saveAuthSettingsButton } from './save-auth-settings-button';
import { internalAuth } from './internal-auth';
export default angular
.module('portainer.settings.authentication', [ldapModule])
.component('internalAuth', internalAuth)
.component('saveAuthSettingsButton', saveAuthSettingsButton)
.component('autoUserProvisionToggle', autoUserProvisionToggle).name;

View File

@ -0,0 +1,7 @@
export const internalAuth = {
templateUrl: './internal-auth.html',
bindings: {
onSaveSettings: '<',
saveButtonState: '<',
},
};

View File

@ -0,0 +1,8 @@
<div class="col-sm-12 form-section-title">
Information
</div>
<div class="form-group col-sm-12 text-muted small">
When using internal authentication, Portainer will encrypt user passwords and store credentials locally.
</div>
<save-auth-settings-button on-save-settings="($ctrl.onSaveSettings)" save-button-state="($ctrl.saveButtonState)"></save-auth-settings-button>

View File

@ -3,8 +3,9 @@ import { HIDE_INTERNAL_AUTH } from '@/portainer/feature-flags/feature-ids';
export default class AdSettingsController {
/* @ngInject */
constructor(LDAPService) {
constructor(LDAPService, featureService) {
this.LDAPService = LDAPService;
this.featureService = featureService;
this.domainSuffix = '';
this.limitedFeatureId = HIDE_INTERNAL_AUTH;
@ -55,6 +56,10 @@ export default class AdSettingsController {
this.settings.URLs.splice(index, 1);
}
isSaveSettingButtonDisabled() {
return this.featureService.isLimitedToBE(this.limitedFeatureId) || !this.isLdapFormValid();
}
$onInit() {
this.tlscaCert = this.settings.TLSCACert;
this.parseDomainName(this.settings.ReaderDN);

View File

@ -160,7 +160,14 @@
selected-admin-groups="$ctrl.selectedAdminGroups"
default-admin-group-search-filter="'(objectClass=groupOfNames)'"
limited-feature-id="$ctrl.limitedFeatureId"
is-limited-feature-self-contained="false"
></ldap-custom-admin-group>
<ldap-settings-test-login settings="$ctrl.settings" limited-feature-id="$ctrl.limitedFeatureId"></ldap-settings-test-login>
<ldap-settings-test-login settings="$ctrl.settings" limited-feature-id="$ctrl.limitedFeatureId" is-limited-feature-self-contained="false"></ldap-settings-test-login>
<save-auth-settings-button
on-save-settings="($ctrl.onSaveSettings)"
save-button-state="($ctrl.saveButtonState)"
limited-feature-id="$ctrl.limitedFeatureId"
save-button-disabled="($ctrl.isSaveSettingButtonDisabled())"
></save-auth-settings-button>
</ng-form>

View File

@ -8,5 +8,8 @@ export const adSettings = {
tlscaCert: '=',
state: '=',
connectivityCheck: '<',
onSaveSettings: '<',
saveButtonState: '<',
isLdapFormValid: '&?',
},
};

View File

@ -1,10 +1,10 @@
<div class="form-group">
<label for="ldap_password" class="col-sm-3 control-label text-left">
<label for="ldap_password" class="col-sm-3 col-lg-2 control-label text-left">
Connectivity check
<i class="fa fa-check green-icon" style="margin-left: 5px;" ng-if="$ctrl.state.successfulConnectivityCheck"></i>
<i class="fa fa-times red-icon" style="margin-left: 5px;" ng-if="$ctrl.state.failedConnectivityCheck"></i>
<i class="fa fa-check green-icon m-l-5" ng-if="$ctrl.state.successfulConnectivityCheck"></i>
<i class="fa fa-times red-icon m-l-5" ng-if="$ctrl.state.failedConnectivityCheck"></i>
</label>
<div class="col-sm-9">
<div class="col-sm-9 col-lg-10">
<button
type="button"
class="btn btn-primary btn-sm"

View File

@ -1,3 +1,4 @@
import './ldap-custom-admin-group.css';
import controller from './ldap-custom-admin-group.controller';
export const ldapCustomAdminGroup = {
@ -9,5 +10,6 @@ export const ldapCustomAdminGroup = {
defaultAdminGroupSearchFilter: '<',
onSearchClick: '<',
limitedFeatureId: '<',
isLimitedFeatureSelfContained: '<',
},
};

View File

@ -0,0 +1,4 @@
.ldap-custom-admin-group-title {
float: initial;
margin-top: 30px;
}

View File

@ -1,10 +1,10 @@
<div class="col-sm-12 form-section-title" style="float: initial;">
Auto-populate team admins
<div class="col-sm-12 form-section-title ldap-custom-admin-group-title" ng-style="$ctrl.isLimitedFeatureSelfContained && { 'padding-bottom': '15px' }">
Auto-populate team admins <be-feature-indicator feature="$ctrl.limitedFeatureId" class="space-left" ng-if="$ctrl.isLimitedFeatureSelfContained"></be-feature-indicator>
</div>
<rd-widget ng-repeat="config in $ctrl.settings.AdminGroupSearchSettings | limitTo: (1 - $ctrl.settings.AdminGroupSearchSettings)" style="display: block; margin-bottom: 10px;">
<rd-widget ng-repeat="config in $ctrl.settings.AdminGroupSearchSettings | limitTo: (1 - $ctrl.settings.AdminGroupSearchSettings)">
<rd-widget-body>
<div class="form-group" ng-if="$index > 0" style="margin-bottom: 10px;">
<div class="form-group m-b-10" ng-if="$index > 0">
<span class="col-sm-12 text-muted small">
Extra search configuration
</span>
@ -16,7 +16,17 @@
<portainer-tooltip position="bottom" message="The distinguished name of the element from which the LDAP server will search for groups."></portainer-tooltip>
</label>
<div class="col-sm-8 col-md-4">
<input type="text" class="form-control" id="ldap_admin_group_basedn_{{ $index }}" ng-model="config.GroupBaseDN" placeholder="dc=ldap,dc=domain,dc=tld" />
<input
type="text"
class="form-control"
id="ldap_admin_group_basedn_{{ $index }}"
ng-model="config.GroupBaseDN"
placeholder="dc=ldap,dc=domain,dc=tld"
limited-feature-dir="{{::$ctrl.limitedFeatureId}}"
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
ng-disabled="{{ $ctrl.isLimitedFeatureSelfContained }}"
limited-feature-tabindex="-1"
/>
</div>
<label for="ldap_admin_group_att_{{ $index }}" class="col-sm-4 col-md-2 control-label text-left">
@ -24,7 +34,17 @@
<portainer-tooltip position="bottom" message="LDAP attribute which denotes the group membership."></portainer-tooltip>
</label>
<div class="col-sm-8 col-md-4">
<input type="text" class="form-control" id="ldap_admin_group_att_{{ $index }}" ng-model="config.GroupAttribute" placeholder="member" />
<input
type="text"
class="form-control"
id="ldap_admin_group_att_{{ $index }}"
ng-model="config.GroupAttribute"
placeholder="member"
limited-feature-dir="{{::$ctrl.limitedFeatureId}}"
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
ng-disabled="{{ $ctrl.isLimitedFeatureSelfContained }}"
limited-feature-tabindex="-1"
/>
</div>
</div>
<div class="form-group">
@ -33,7 +53,17 @@
<portainer-tooltip position="bottom" message="The LDAP search filter used to select group elements, optional."></portainer-tooltip>
</label>
<div ng-class="{ 'col-sm-7 col-md-9': $index, 'col-sm-8 col-md-10': !$index }">
<input type="text" class="form-control" id="ldap_admin_group_filter_{{ $index }}" ng-model="config.GroupFilter" placeholder="(objectClass=groupOfNames)" />
<input
type="text"
class="form-control"
id="ldap_admin_group_filter_{{ $index }}"
ng-model="config.GroupFilter"
placeholder="(objectClass=groupOfNames)"
limited-feature-dir="{{::$ctrl.limitedFeatureId}}"
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
ng-disabled="{{ $ctrl.isLimitedFeatureSelfContained }}"
limited-feature-tabindex="-1"
/>
</div>
<div class="col-sm-1" ng-if="$index > 0">
<button class="btn btn-sm btn-danger" type="button" ng-click="$ctrl.onRemoveClick($index)">
@ -44,20 +74,31 @@
</rd-widget-body>
</rd-widget>
<div class="form-group" style="margin-top: 10px;">
<div class="form-group m-t-10">
<div class="col-sm-12">
<button class="label label-default interactive" style="border: 0;" ng-click="$ctrl.onAddClick()">
<button
class="label label-default interactive no-border"
ng-click="$ctrl.onAddClick()"
limited-feature-dir="{{ $ctrl.limitedFeatureId }}"
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
limited-feature-disabled
>
<i class="fa fa-plus-circle" aria-hidden="true"></i> add group search configuration
</button>
</div>
<div class="col-sm-12" style="margin-top: 10px;">
<button class="btn btm-sm btn-primary" type="button" ng-click="$ctrl.search()" limited-feature-dir="{{ $ctrl.limitedFeatureId }}" limited-feature-tabindex="-1">
<div class="col-sm-12 m-t-10">
<button
class="btn btm-sm btn-primary"
type="button"
ng-click="$ctrl.search()"
limited-feature-dir="{{ $ctrl.limitedFeatureId }}"
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
ng-disabled="{{ $ctrl.isLimitedFeatureSelfContained }}"
limited-feature-tabindex="-1"
>
Fetch Admin Group(s)
</button>
<be-feature-indicator feature="$ctrl.limitedFeatureId" class="space-left"></be-feature-indicator>
<span ng-if="$ctrl.groups && $ctrl.groups.length === 0" style="margin-left: 30px;">
<i class="fa fa-exclamation-triangle text-warning" aria-hidden="true"></i> No groups found</span
>
<span ng-if="$ctrl.groups && $ctrl.groups.length === 0" class="m-l-30"> <i class="fa fa-exclamation-triangle text-warning" aria-hidden="true"></i> No groups found</span>
</div>
</div>
@ -66,7 +107,7 @@
<label for="admin-auto-populate" class="control-label text-left text-muted" ng-class="{ 'text-muted': !$ctrl.enableAssignAdminGroup }">
Assign admin rights to group(s)
</label>
<label class="switch" style="margin-left: 20px;">
<label class="switch m-l-20" ng-class="{ 'business limited': $ctrl.isLimitedFeatureSelfContained }">
<input id="admin-auto-populate" ng-disabled="!$ctrl.enableAssignAdminGroup" name="admin-auto-populate" type="checkbox" ng-model="$ctrl.settings.AdminAutoPopulate" /><i></i>
</label>
</div>
@ -78,6 +119,7 @@
Select Group(s)
</label>
<span
class="m-l-20"
isteven-multi-select
ng-if="$ctrl.enableAssignAdminGroup"
input-model="$ctrl.groups"
@ -88,7 +130,6 @@
helper-elements="filter"
search-property="name"
translation="{nothingSelected: 'Select one or more groups', search: 'Search...'}"
style="margin-left: 20px;"
>
</span>
</div>

View File

@ -1,3 +1,4 @@
import './ldap-settings-custom.css';
import controller from './ldap-settings-custom.controller';
export const ldapSettingsCustom = {
@ -11,5 +12,8 @@ export const ldapSettingsCustom = {
connectivityCheck: '<',
onSearchUsersClick: '<',
onSearchGroupsClick: '<',
onSaveSettings: '<',
saveButtonState: '<',
saveButtonDisabled: '<',
},
};

View File

@ -1,5 +1,4 @@
import { EXTERNAL_AUTH_LDAP } from '@/portainer/feature-flags/feature-ids';
export default class LdapSettingsCustomController {
constructor() {
this.limitedFeatureId = EXTERNAL_AUTH_LDAP;

View File

@ -0,0 +1,3 @@
label[for='anonymous_mode'].control-label {
padding-top: 0;
}

View File

@ -14,7 +14,7 @@
<div class="form-group">
<div class="col-sm-12 small text-muted">
<p>
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
<i class="fa fa-info-circle blue-icon m-r-2" aria-hidden="true"></i>
You can configure multiple LDAP Servers for authentication fallback. Make sure all servers are using the same configuration (i.e. if TLS is enabled, they should all use the
same certificates).
</p>
@ -22,12 +22,11 @@
</div>
<div class="form-group">
<label for="ldap_url" class="col-sm-3 col-lg-2 control-label text-left" style="display: flex; flex-wrap: wrap;">
<label for="ldap_url" class="col-sm-3 col-lg-2 control-label text-left dispay-flex flex-wrap">
LDAP Server
<button
type="button"
class="label label-default interactive"
style="border: 0;"
class="label label-default interactive no-border"
ng-click="$ctrl.addLDAPUrl()"
limited-feature-dir="{{ $ctrl.limitedFeatureId }}"
limited-feature-disabled
@ -37,7 +36,7 @@
</button>
</label>
<div class="col-sm-9 col-lg-10">
<div ng-repeat="url in $ctrl.settings.URLs track by $index" style="display: flex; margin-bottom: 10px;">
<div class="m-b-10 dispay-flex" ng-repeat="url in $ctrl.settings.URLs track by $index">
<input type="text" class="form-control" id="ldap_url" ng-model="$ctrl.settings.URLs[$index]" placeholder="e.g. 10.0.0.10:389 or myldap.domain.tld:389" required />
<button ng-if="$index > 0" class="btn btn-sm btn-danger" type="button" ng-click="$ctrl.removeLDAPUrl($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
@ -46,14 +45,15 @@
</div>
</div>
<!-- Anonymous mode-->
<div class="form-group">
<div class="col-sm-12">
<label for="anonymous_mode" class="control-label text-left">
Anonymous mode
<portainer-tooltip position="bottom" message="Enable this option if the server is configured for Anonymous access."></portainer-tooltip>
<label for="anonymous_mode" class="control-label text-left col-sm-3 col-lg-2">
Anonymous mode
<portainer-tooltip position="bottom" message="Enable this option if the server is configured for Anonymous access."></portainer-tooltip>
</label>
<div class="col-sm-9 col-lg-10">
<label class="switch">
<input type="checkbox" id="anonymous_mode" ng-model="$ctrl.settings.AnonymousMode" limited-feature-dir="{{::$ctrl.limitedFeatureId}}" limited-feature-tabindex="-1" /><i></i>
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" id="anonymous_mode" ng-model="$ctrl.settings.AnonymousMode" /><i></i> </label>
</div>
</div>
<!-- !Anonymous mode-->
@ -102,29 +102,38 @@
></ldap-connectivity-check>
<ldap-custom-user-search
style="margin-top: 5px;"
class="m-r-5"
settings="$ctrl.settings.SearchSettings"
on-search-click="($ctrl.onSearchUsersClick)"
limited-feature-id="$ctrl.limitedFeatureId"
></ldap-custom-user-search>
<ldap-custom-group-search
style="margin-top: 5px;"
class="m-r-5"
settings="$ctrl.settings.GroupSearchSettings"
on-search-click="($ctrl.onSearchGroupsClick)"
limited-feature-id="$ctrl.limitedFeatureId"
></ldap-custom-group-search>
<div limited-feature-dir="{{ $ctrl.limitedFeatureId }}" limited-feature-class="limited-be" style="margin-bottom: 20px;">
<ldap-custom-admin-group
style="margin-top: 5px;"
settings="$ctrl.settings"
on-search-click="($ctrl.onSearchAdminGroupsClick)"
selected-admin-groups="$ctrl.selectedAdminGroups"
default-admin-group-search-filter="'(objectClass=groupOfNames)'"
limited-feature-id="$ctrl.limitedFeatureId"
></ldap-custom-admin-group>
</div>
<ldap-custom-admin-group
class="m-r-5"
settings="$ctrl.settings"
on-search-click="($ctrl.onSearchAdminGroupsClick)"
selected-admin-groups="$ctrl.selectedAdminGroups"
default-admin-group-search-filter="'(objectClass=groupOfNames)'"
limited-feature-id="$ctrl.limitedFeatureId"
is-limited-feature-self-contained="true"
></ldap-custom-admin-group>
<div limited-feature-dir="{{ $ctrl.limitedFeatureId }}" limited-feature-class="limited-be">
<ldap-settings-test-login settings="$ctrl.settings" limited-feature-id="$ctrl.limitedFeatureId" show-be-indicator-if-needed="true"></ldap-settings-test-login>
</div>
<ldap-settings-test-login
settings="$ctrl.settings"
limited-feature-id="$ctrl.limitedFeatureId"
show-be-indicator-if-needed="true"
is-limited-feature-self-contained="true"
></ldap-settings-test-login>
<save-auth-settings-button
on-save-settings="($ctrl.onSaveSettings)"
save-button-state="($ctrl.saveButtonState)"
save-button-disabled="!$ctrl.saveButtonDisabled()"
limited-feature-dir="{{ $ctrl.limitedFeatureId }}"
></save-auth-settings-button>

View File

@ -8,9 +8,11 @@ export const ldapSettingsOpenLdap = {
tlscaCert: '=',
state: '=',
connectivityCheck: '<',
onTlscaCertChange: '<',
onSearchUsersClick: '<',
onSearchGroupsClick: '<',
onSaveSettings: '<',
saveButtonState: '<',
saveButtonDisabled: '<',
},
};

View File

@ -184,4 +184,10 @@
></ldap-group-search>
<ldap-settings-test-login settings="$ctrl.settings" limited-feature-id="$ctrl.limitedFeatureId"></ldap-settings-test-login>
<save-auth-settings-button
on-save-settings="($ctrl.onSaveSettings)"
save-button-state="($ctrl.saveButtonState)"
save-button-disabled="!$ctrl.saveButtonDisabled()"
limited-feature-id="$ctrl.limitedFeatureId"
></save-auth-settings-button>
</ng-form>

View File

@ -1,3 +1,4 @@
import './ldap-settings-test-login.css';
import controller from './ldap-settings-test-login.controller';
export const ldapSettingsTestLogin = {
@ -7,5 +8,6 @@ export const ldapSettingsTestLogin = {
settings: '=',
limitedFeatureId: '<',
showBeIndicatorIfNeeded: '<',
isLimitedFeatureSelfContained: '<',
},
};

View File

@ -0,0 +1,4 @@
label[for='ldap_test_password'] {
font-size: 0.9em;
margin-right: 5px;
}

View File

@ -1,16 +1,31 @@
<div class="col-sm-12 form-section-title">
<div class="col-sm-12 form-section-title" ng-style="$ctrl.isLimitedFeatureSelfContained && { 'padding-bottom': '15px' }">
Test login
<be-feature-indicator
ng-if="$ctrl.showBeIndicatorIfNeeded"
feature="$ctrl.limitedFeatureId"
class="space-left"
ng-if="$ctrl.isLimitedFeatureSelfContained"
></be-feature-indicator>
</div>
<div class="form-inline">
<div class="form-group" style="margin: 0;">
<label for="ldap_test_username" style="font-size: 0.9em; margin-right: 5px;">
Username
</label>
<input type="text" class="form-control" id="ldap_test_username" ng-model="$ctrl.username" limited-feature-dir="{{::$ctrl.limitedFeatureId}}" limited-feature-tabindex="-1" />
<input
type="text"
class="form-control"
id="ldap_test_username"
ng-model="$ctrl.username"
limited-feature-dir="{{::$ctrl.limitedFeatureId}}"
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
ng-disabled="{{ $ctrl.isLimitedFeatureSelfContained }}"
limited-feature-tabindex="-1"
/>
</div>
<div class="form-group" style="margin: 0;">
<label for="ldap_test_password" style="font-size: 0.9em; margin-right: 5px;">
<div class="form-group no-margin">
<label for="ldap_test_password">
Password
</label>
<input
@ -20,18 +35,20 @@
ng-model="$ctrl.password"
autocomplete="new-password"
limited-feature-dir="{{::$ctrl.limitedFeatureId}}"
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
ng-disabled="{{ $ctrl.isLimitedFeatureSelfContained }}"
limited-feature-tabindex="-1"
/>
</div>
<div class="form-group" style="margin: 0;">
<div class="form-group no-margin">
<button
type="submit"
class="btn btn-primary"
ng-disabled="$ctrl.state.testStatus === $ctrl.TEST_STATUS.LOADING || !$ctrl.username || !$ctrl.password"
ng-click="$ctrl.testLogin($ctrl.username, $ctrl.password)"
limited-feature-dir="{{::$ctrl.limitedFeatureId}}"
limited-feature-disabled
limited-feature-class=" {{ $ctrl.isLimitedFeatureSelfContained && 'limited-be' }}"
limited-feature-tabindex="-1"
>
<span ng-if="$ctrl.state.testStatus !== $ctrl.TEST_STATUS.LOADING">Test</span>
@ -40,6 +57,4 @@
<i ng-if="$ctrl.state.testStatus === $ctrl.TEST_STATUS.SUCCESS" class="fa fa-check green-icon"></i>
<i ng-if="$ctrl.state.testStatus === $ctrl.TEST_STATUS.FAILURE" class="fa fa-times red-icon"></i>
</div>
<be-feature-indicator ng-if="$ctrl.showBeIndicatorIfNeeded" feature="$ctrl.limitedFeatureId" class="space-left"></be-feature-indicator>
</div>

View File

@ -7,5 +7,8 @@ export const ldapSettings = {
settings: '=',
state: '<',
connectivityCheck: '<',
onSaveSettings: '<',
saveButtonState: '<',
isLdapFormValid: '<',
},
};

View File

@ -27,6 +27,9 @@
connectivity-check="$ctrl.connectivityCheck"
on-search-users-click="($ctrl.searchUsers)"
on-search-groups-click="($ctrl.searchGroups)"
on-save-settings="($ctrl.onSaveSettings)"
save-button-state="($ctrl.saveButtonState)"
save-button-disabled="$ctrl.isLdapFormValid"
></ldap-settings-custom>
<ldap-settings-open-ldap
ng-if="$ctrl.settings.ServerType === $ctrl.SERVER_TYPES.OPEN_LDAP"
@ -37,5 +40,8 @@
connectivity-check="$ctrl.connectivityCheck"
on-search-users-click="($ctrl.searchUsers)"
on-search-groups-click="($ctrl.searchGroups)"
on-save-settings="($ctrl.onSaveSettings)"
save-button-state="($ctrl.saveButtonState)"
save-button-disabled="$ctrl.isLdapFormValid"
></ldap-settings-open-ldap>
</div>

View File

@ -0,0 +1,11 @@
export const saveAuthSettingsButton = {
templateUrl: './save-auth-settings-button.html',
bindings: {
onSaveSettings: '<',
saveButtonDisabled: '<',
saveButtonState: '<',
limitedFeatureId: '<',
limitedFeatureClass: '<',
className: '<',
},
};

View File

@ -0,0 +1,19 @@
<div class="col-sm-12 form-section-title">
Actions
</div>
<div class="form-group">
<div class="col-sm-12">
<button
type="button"
ng-class="[$ctrl.className, 'btn btn-primary btn-sm']"
ng-click="$ctrl.onSaveSettings()"
ng-disabled="$ctrl.saveButtonDisabled || $ctrl.saveButtonState"
button-spinner="$ctrl.saveButtonState"
limited-feature-dir="{{::$ctrl.limitedFeatureId}}"
limited-feature-class="{{::$ctrl.limitedFeatureClass}}"
>
<span ng-hide="$ctrl.saveButtonState">Save settings</span>
<span ng-show="$ctrl.saveButtonState">Saving...</span>
</button>
</div>
</div>

View File

@ -25,7 +25,9 @@
</p>
<div>
<button type="button" class="btn btn-sm btn-primary" style="margin-right: 10px;"> <i class="fa fa-download space-right" aria-hidden="true"></i>Export as CSV </button>
<button type="button" class="btn btn-sm btn-primary m-r-10" limited-feature-dir="{{::$ctrl.limitedFeature}}" limited-feature-class="limited-be" limited-feature-disabled>
<i class="fa fa-download space-right" aria-hidden="true"></i>Export as CSV
</button>
<be-feature-indicator feature="$ctrl.limitedFeature"></be-feature-indicator>
</div>
</rd-widget-body>

View File

@ -25,7 +25,9 @@
</p>
<div>
<button type="button" class="btn btn-sm btn-primary" style="margin-right: 10px;"><i class="fa fa-download space-right" aria-hidden="true"></i>Export as CSV </button>
<button type="button" class="btn btn-sm btn-primary m-r-10" limited-feature-dir="{{::$ctrl.limitedFeature}}" limited-feature-class="limited-be" limited-feature-disabled
><i class="fa fa-download space-right" aria-hidden="true"></i>Export as CSV
</button>
<be-feature-indicator feature="$ctrl.limitedFeature"></be-feature-indicator>
</div>
</rd-widget-body>

View File

@ -37,14 +37,7 @@
<box-selector radio-name="authOptions" ng-model="authMethod" options="authOptions" on-change="(onChangeAuthMethod)"></box-selector>
<div ng-if="authenticationMethodSelected(1)">
<div class="col-sm-12 form-section-title">
Information
</div>
<div class="form-group col-sm-12 text-muted small">
When using internal authentication, Portainer will encrypt user passwords and store credentials locally.
</div>
</div>
<internal-auth ng-if="authenticationMethodSelected(1)" on-save-settings="(saveSettings)" save-button-state="state.actionInProgress"></internal-auth>
<ldap-settings
ng-if="authenticationMethodSelected(2)"
@ -52,6 +45,9 @@
tlsca-cert="formValues.TLSCACert"
state="state"
connectivity-check="LDAPConnectivityCheck"
on-save-settings="(saveSettings)"
save-button-state="state.actionInProgress"
is-ldap-form-valid="isLDAPFormValid"
></ldap-settings>
<ad-settings
@ -60,29 +56,18 @@
tlsca-cert="formValues.TLSCACert"
state="state"
connectivity-check="LDAPConnectivityCheck"
on-save-settings="(saveSettings)"
save-button-state="state.actionInProgress"
is-ldap-form-valid="isLDAPFormValid()"
></ad-settings>
<oauth-settings ng-if="authenticationMethodSelected(3)" settings="OAuthSettings" teams="teams"></oauth-settings>
<!-- actions -->
<div class="col-sm-12 form-section-title">
Actions
</div>
<div class="form-group">
<div class="col-sm-12">
<button
type="button"
class="btn btn-primary btn-sm"
ng-click="saveSettings()"
ng-disabled="state.actionInProgress || (settings.AuthenticationMethod === 2 && !isLDAPFormValid()) || (settings.AuthenticationMethod === 3 && !isOAuthTeamMembershipFormValid()) || authSettingsForm.$invalid"
button-spinner="state.actionInProgress"
>
<span ng-hide="state.actionInProgress">Save settings</span>
<span ng-show="state.actionInProgress">Saving...</span>
</button>
</div>
</div>
<!-- !actions -->
<oauth-settings
ng-if="authenticationMethodSelected(3)"
settings="OAuthSettings"
teams="teams"
on-save-settings="(saveSettings)"
save-button-state="state.actionInProgress"
></oauth-settings>
</form>
</rd-widget-body>
</rd-widget>

View File

@ -182,7 +182,8 @@ function SettingsAuthenticationController($q, $scope, $state, Notifications, Set
return (
_.compact(ldapSettings.URLs).length &&
(ldapSettings.AnonymousMode || (ldapSettings.ReaderDN && ldapSettings.Password)) &&
(!isTLSMode || $scope.formValues.TLSCACert || ldapSettings.TLSConfig.TLSSkipVerify)
(!isTLSMode || $scope.formValues.TLSCACert || ldapSettings.TLSConfig.TLSSkipVerify) &&
(!$scope.settings.LDAPSettings.AdminAutoPopulate || ($scope.settings.LDAPSettings.AdminAutoPopulate && $scope.formValues.selectedAdminGroups.length > 0))
);
}