diff --git a/app/assets/css/app.css b/app/assets/css/app.css
index 91b058fe2..1b7693261 100644
--- a/app/assets/css/app.css
+++ b/app/assets/css/app.css
@@ -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;
diff --git a/app/portainer/feature-flags/feature-flags.css b/app/portainer/feature-flags/feature-flags.css
index 9ffda18d8..600296aee 100644
--- a/app/portainer/feature-flags/feature-flags.css
+++ b/app/portainer/feature-flags/feature-flags.css
@@ -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;
diff --git a/app/portainer/oauth/components/oauth-settings/index.js b/app/portainer/oauth/components/oauth-settings/index.js
index c7fc2712f..fb452d8cd 100644
--- a/app/portainer/oauth/components/oauth-settings/index.js
+++ b/app/portainer/oauth/components/oauth-settings/index.js
@@ -6,6 +6,8 @@ angular.module('portainer.oauth').component('oauthSettings', {
bindings: {
settings: '=',
teams: '<',
+ onSaveSettings: '<',
+ saveButtonState: '<',
},
controller,
});
diff --git a/app/portainer/oauth/components/oauth-settings/oauth-settings.controller.js b/app/portainer/oauth/components/oauth-settings/oauth-settings.controller.js
index 500560ad7..c67a06be0 100644
--- a/app/portainer/oauth/components/oauth-settings/oauth-settings.controller.js
+++ b/app/portainer/oauth/components/oauth-settings/oauth-settings.controller.js
@@ -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);
diff --git a/app/portainer/oauth/components/oauth-settings/oauth-settings.html b/app/portainer/oauth/components/oauth-settings/oauth-settings.html
index 4853aabc1..8116cd999 100644
--- a/app/portainer/oauth/components/oauth-settings/oauth-settings.html
+++ b/app/portainer/oauth/components/oauth-settings/oauth-settings.html
@@ -1,4 +1,4 @@
-
+
Single Sign-On
@@ -379,4 +379,12 @@
+
diff --git a/app/portainer/settings/authentication/index.js b/app/portainer/settings/authentication/index.js
index 897e48042..02b21a57f 100644
--- a/app/portainer/settings/authentication/index.js
+++ b/app/portainer/settings/authentication/index.js
@@ -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;
diff --git a/app/portainer/settings/authentication/internal-auth/index.js b/app/portainer/settings/authentication/internal-auth/index.js
new file mode 100644
index 000000000..1d4b2c595
--- /dev/null
+++ b/app/portainer/settings/authentication/internal-auth/index.js
@@ -0,0 +1,7 @@
+export const internalAuth = {
+ templateUrl: './internal-auth.html',
+ bindings: {
+ onSaveSettings: '<',
+ saveButtonState: '<',
+ },
+};
diff --git a/app/portainer/settings/authentication/internal-auth/internal-auth.html b/app/portainer/settings/authentication/internal-auth/internal-auth.html
new file mode 100644
index 000000000..b54ee2fb1
--- /dev/null
+++ b/app/portainer/settings/authentication/internal-auth/internal-auth.html
@@ -0,0 +1,8 @@
+
+ Information
+
+
+ When using internal authentication, Portainer will encrypt user passwords and store credentials locally.
+
+
+
diff --git a/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.controller.js b/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.controller.js
index 41d29e0ef..969388459 100644
--- a/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.controller.js
+++ b/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.controller.js
@@ -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);
diff --git a/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.html b/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.html
index 0f022ae7c..48bf2c375 100644
--- a/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.html
+++ b/app/portainer/settings/authentication/ldap/ad-settings/ad-settings.html
@@ -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"
>
-
+
+
diff --git a/app/portainer/settings/authentication/ldap/ad-settings/index.js b/app/portainer/settings/authentication/ldap/ad-settings/index.js
index 59a474097..5308496d7 100644
--- a/app/portainer/settings/authentication/ldap/ad-settings/index.js
+++ b/app/portainer/settings/authentication/ldap/ad-settings/index.js
@@ -8,5 +8,8 @@ export const adSettings = {
tlscaCert: '=',
state: '=',
connectivityCheck: '<',
+ onSaveSettings: '<',
+ saveButtonState: '<',
+ isLdapFormValid: '&?',
},
};
diff --git a/app/portainer/settings/authentication/ldap/ldap-connectivity-check/ldap-connectivity-check.html b/app/portainer/settings/authentication/ldap/ldap-connectivity-check/ldap-connectivity-check.html
index 0d88ade53..fa6381b32 100644
--- a/app/portainer/settings/authentication/ldap/ldap-connectivity-check/ldap-connectivity-check.html
+++ b/app/portainer/settings/authentication/ldap/ldap-connectivity-check/ldap-connectivity-check.html
@@ -1,10 +1,10 @@