refactor(settings/auth): migrate auto user provision toggle to react [BE-12585] (#1865)
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,13 @@ export default class OAuthSettingsController {
|
||||
this.removeTeamMembership = this.removeTeamMembership.bind(this);
|
||||
this.onToggleAutoTeamMembership = this.onToggleAutoTeamMembership.bind(this);
|
||||
this.onChangeAuthStyle = this.onChangeAuthStyle.bind(this);
|
||||
this.onAutoUserProvisionChange = this.onAutoUserProvisionChange.bind(this);
|
||||
}
|
||||
|
||||
onAutoUserProvisionChange(value) {
|
||||
this.$scope.$evalAsync(() => {
|
||||
this.settings.OAuthAutoCreateUsers = value;
|
||||
});
|
||||
}
|
||||
|
||||
onMicrosoftTenantIDChange() {
|
||||
|
||||
@@ -29,12 +29,11 @@
|
||||
</div>
|
||||
<!-- !HideInternalAuth -->
|
||||
|
||||
<auto-user-provision-toggle ng-model="$ctrl.settings.OAuthAutoCreateUsers">
|
||||
<field-description>
|
||||
With automatic user provisioning enabled, Portainer will create user(s) automatically with the standard user role. If disabled, users must be created beforehand in Portainer
|
||||
in order to login.
|
||||
</field-description>
|
||||
</auto-user-provision-toggle>
|
||||
<auto-user-provision-toggle
|
||||
value="$ctrl.settings.OAuthAutoCreateUsers"
|
||||
on-change="($ctrl.onAutoUserProvisionChange)"
|
||||
description="'With automatic user provisioning enabled, Portainer will create user(s) automatically with the standard user role. If disabled, users must be created beforehand in Portainer in order to login.'"
|
||||
></auto-user-provision-toggle>
|
||||
|
||||
<div ng-if="$ctrl.settings.OAuthAutoCreateUsers">
|
||||
<div class="form-group">
|
||||
|
||||
@@ -14,6 +14,7 @@ import { HelmCertPanel } from '@/react/portainer/settings/SettingsView/HelmCertP
|
||||
import { HiddenContainersPanel } from '@/react/portainer/settings/SettingsView/HiddenContainersPanel/HiddenContainersPanel';
|
||||
import { SSLSettingsPanelWrapper } from '@/react/portainer/settings/SettingsView/SSLSettingsPanel/SSLSettingsPanel';
|
||||
import { AuthStyleField } from '@/react/portainer/settings/AuthenticationView/OAuth';
|
||||
import { AutoUserProvisionToggle } from '@/react/portainer/settings/AuthenticationView/AutoUserProvisionToggle/AutoUserProvisionToggle';
|
||||
|
||||
export const settingsModule = angular
|
||||
.module('portainer.app.react.components.settings', [])
|
||||
@@ -55,4 +56,13 @@ export const settingsModule = angular
|
||||
'readonly',
|
||||
'size',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'autoUserProvisionToggle',
|
||||
r2a(AutoUserProvisionToggle, [
|
||||
'value',
|
||||
'onChange',
|
||||
'description',
|
||||
'data-cy',
|
||||
])
|
||||
).name;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<div class="col-sm-12 form-section-title"> Automatic user provisioning </div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small" ng-transclude="description"> </span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 vertical-center">
|
||||
<label for="tls" class="control-label !pt-0 text-left"> Automatic user provisioning </label>
|
||||
<label class="switch my-0 ml-6">
|
||||
<input type="checkbox" ng-model="$ctrl.ngModel" data-cy="auto-user-provision-toggle" />
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,9 +0,0 @@
|
||||
export const autoUserProvisionToggle = {
|
||||
templateUrl: './auto-user-provision-toggle.html',
|
||||
transclude: {
|
||||
description: 'fieldDescription',
|
||||
},
|
||||
bindings: {
|
||||
ngModel: '=',
|
||||
},
|
||||
};
|
||||
@@ -1,10 +1,8 @@
|
||||
import angular from 'angular';
|
||||
import ldapModule from './ldap';
|
||||
import { autoUserProvisionToggle } from './auto-user-provision-toggle';
|
||||
import { saveAuthSettingsButton } from './save-auth-settings-button';
|
||||
|
||||
export default angular
|
||||
.module('portainer.settings.authentication', [ldapModule])
|
||||
|
||||
.component('saveAuthSettingsButton', saveAuthSettingsButton)
|
||||
.component('autoUserProvisionToggle', autoUserProvisionToggle).name;
|
||||
.component('saveAuthSettingsButton', saveAuthSettingsButton).name;
|
||||
|
||||
@@ -5,8 +5,9 @@ import { isLimitedToBE } from '@/react/portainer/feature-flags/feature-flags.ser
|
||||
|
||||
export default class AdSettingsController {
|
||||
/* @ngInject */
|
||||
constructor(LDAPService) {
|
||||
constructor(LDAPService, $scope) {
|
||||
this.LDAPService = LDAPService;
|
||||
this.$scope = $scope;
|
||||
|
||||
this.domainSuffix = '';
|
||||
this.limitedFeatureId = FeatureId.HIDE_INTERNAL_AUTH;
|
||||
@@ -15,6 +16,14 @@ export default class AdSettingsController {
|
||||
this.searchGroups = this.searchGroups.bind(this);
|
||||
this.parseDomainName = this.parseDomainName.bind(this);
|
||||
this.onAccountChange = this.onAccountChange.bind(this);
|
||||
this.onAutoUserProvisionChange = this.onAutoUserProvisionChange.bind(this);
|
||||
this.onAutoUserProvisionChange = this.onAutoUserProvisionChange.bind(this);
|
||||
}
|
||||
|
||||
onAutoUserProvisionChange(value) {
|
||||
this.$scope.$evalAsync(() => {
|
||||
this.settings.AutoCreateUsers = value;
|
||||
});
|
||||
}
|
||||
|
||||
parseDomainName(account) {
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
<div class="be-indicator-container">
|
||||
<div class="limited-be-link vertical-center"><be-feature-indicator feature="$ctrl.limitedFeatureId"></be-feature-indicator></div>
|
||||
<div class="limited-be-content">
|
||||
<auto-user-provision-toggle ng-model="$ctrl.settings.AutoCreateUsers">
|
||||
<field-description>
|
||||
With automatic user provisioning enabled, Portainer will create user(s) automatically with standard user role and assign them to team(s) which matches to LDAP group
|
||||
name(s). If disabled, users must be created in Portainer beforehand.
|
||||
</field-description>
|
||||
</auto-user-provision-toggle>
|
||||
<auto-user-provision-toggle
|
||||
value="$ctrl.settings.AutoCreateUsers"
|
||||
on-change="($ctrl.onAutoUserProvisionChange)"
|
||||
description="'With automatic user provisioning enabled, Portainer will create user(s) automatically with standard user role and assign them to team(s) which matches to LDAP group name(s). If disabled, users must be created in Portainer beforehand.'"
|
||||
></auto-user-provision-toggle>
|
||||
|
||||
<div>
|
||||
<div class="col-sm-12 form-section-title"> Information </div>
|
||||
|
||||
@@ -12,8 +12,8 @@ const DEFAULT_USER_FILTER = '(objectClass=inetOrgPerson)';
|
||||
|
||||
export default class LdapSettingsController {
|
||||
/* @ngInject */
|
||||
constructor(LDAPService) {
|
||||
Object.assign(this, { LDAPService, SERVER_TYPES });
|
||||
constructor(LDAPService, $scope) {
|
||||
Object.assign(this, { LDAPService, SERVER_TYPES, $scope });
|
||||
|
||||
this.tlscaCert = null;
|
||||
this.settingsDrafts = {};
|
||||
@@ -24,8 +24,15 @@ export default class LdapSettingsController {
|
||||
this.searchUsers = this.searchUsers.bind(this);
|
||||
this.searchGroups = this.searchGroups.bind(this);
|
||||
this.onChangeServerType = this.onChangeServerType.bind(this);
|
||||
this.onAutoUserProvisionChange = this.onAutoUserProvisionChange.bind(this);
|
||||
this.onAutoUserProvisionChange = this.onAutoUserProvisionChange.bind(this);
|
||||
}
|
||||
|
||||
onAutoUserProvisionChange(value) {
|
||||
this.$scope.$evalAsync(() => {
|
||||
this.settings.AutoCreateUsers = value;
|
||||
});
|
||||
}
|
||||
onTlscaCertChange(file) {
|
||||
this.tlscaCert = file;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<div>
|
||||
<auto-user-provision-toggle ng-model="$ctrl.settings.AutoCreateUsers">
|
||||
<field-description>
|
||||
With automatic user provisioning enabled, Portainer will create user(s) automatically with standard user role and assign them to team(s) which matches to LDAP group name(s).
|
||||
If disabled, users must be created in Portainer beforehand.
|
||||
</field-description>
|
||||
</auto-user-provision-toggle>
|
||||
<auto-user-provision-toggle
|
||||
value="$ctrl.settings.AutoCreateUsers"
|
||||
on-change="($ctrl.onAutoUserProvisionChange)"
|
||||
description="'With automatic user provisioning enabled, Portainer will create user(s) automatically with standard user role and assign them to team(s) which matches to LDAP group name(s). If disabled, users must be created in Portainer beforehand.'"
|
||||
></auto-user-provision-toggle>
|
||||
|
||||
<div class="col-sm-12 form-section-title"> Server Type </div>
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ BE-6604 (Parent: Authentication Settings Migration)
|
||||
|
||||
### [BE-12593: LDAP Settings](https://linear.app/portainer/issue/BE-12593) (Parent Issue)
|
||||
|
||||
- [ ] PR 3: [BE-12585](https://linear.app/portainer/issue/BE-12585) AutoUserProvisionToggle → shared component
|
||||
- [x] PR 3: [BE-12585](https://linear.app/portainer/issue/BE-12585) AutoUserProvisionToggle → shared component
|
||||
- **LDAP Shared Components** (Parallel - No Dependencies)
|
||||
- [ ] PR 4: [BE-12586](https://linear.app/portainer/issue/BE-12586) LdapSettingsDnBuilder
|
||||
- [ ] PR 5: [BE-12587](https://linear.app/portainer/issue/BE-12587) LdapSettingsGroupDnBuilder
|
||||
@@ -190,31 +190,6 @@ BE-6604 (Parent: Authentication Settings Migration)
|
||||
- [ ] PR 19: [BE-12601](https://linear.app/portainer/issue/BE-12601) AuthenticationView container (Formik) - **Blocked by BE-12593, BE-12596, BE-12600**
|
||||
- [ ] PR 20: [BE-12602](https://linear.app/portainer/issue/BE-12602) Complete React migration - **Blocked by PR 19**
|
||||
|
||||
## Current PR: PR 2 - Completed ✅
|
||||
|
||||
**What was implemented:**
|
||||
|
||||
- Created `AuthenticationMethodSelector.tsx` component in both CE and EE
|
||||
- Component wraps BoxSelector with authentication method options
|
||||
- Registered component via react2angular bridge in `portainer/react/components/settings.ts`
|
||||
- Updated Angular templates (`settingsAuthentication.html`) to use `<authentication-method-selector>`
|
||||
- Added comprehensive unit tests with 8 test cases covering all authentication methods
|
||||
- Tests use `userEvent`, `toBeVisible()`, and proper accessibility queries
|
||||
|
||||
**Files changed:**
|
||||
|
||||
- `package/server-ce/app/react/portainer/settings/AuthenticationView/AuthenticationMethodSelector.tsx` (new)
|
||||
- `package/server-ce/app/react/portainer/settings/AuthenticationView/AuthenticationMethodSelector.test.tsx` (new)
|
||||
- `package/server-ce/app/portainer/react/components/settings.ts` (modified)
|
||||
- `package/server-ce/app/portainer/views/settings/authentication/settingsAuthentication.html` (modified)
|
||||
- `package/server-ee/app/react/portainer/settings/AuthenticationView/AuthenticationMethodSelector.tsx` (new)
|
||||
- `package/server-ee/app/react/portainer/settings/AuthenticationView/AuthenticationMethodSelector.test.tsx` (new)
|
||||
- `package/server-ee/app/portainer/react/components/settings.ts` (modified)
|
||||
- `package/server-ee/app/portainer/views/settings/authentication/settingsAuthentication.html` (modified)
|
||||
|
||||
**Integration:** Seamless - replaced `<box-selector>` with `<authentication-method-selector>` in templates
|
||||
**Status:** Completed ✅
|
||||
|
||||
## Linear Issue Comparison
|
||||
|
||||
The Linear issue BE-6604 provides a detailed breakdown of the component structure. Our migration plan aligns with it:
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { AutoUserProvisionToggle } from './AutoUserProvisionToggle';
|
||||
|
||||
describe('AutoUserProvisionToggle', () => {
|
||||
test('renders with description', () => {
|
||||
render(
|
||||
<AutoUserProvisionToggle
|
||||
value={false}
|
||||
onChange={() => {}}
|
||||
description="Test description for automatic user provisioning"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText(/test description for automatic user provisioning/i)
|
||||
).toBeVisible();
|
||||
expect(
|
||||
screen.getByRole('checkbox', { name: /automatic user provisioning/i })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('calls onChange when toggled', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onChange = vi.fn();
|
||||
|
||||
render(
|
||||
<AutoUserProvisionToggle
|
||||
value={false}
|
||||
onChange={onChange}
|
||||
description="Enable automatic user provisioning"
|
||||
/>
|
||||
);
|
||||
|
||||
const toggle = screen.getByRole('checkbox', {
|
||||
name: /automatic user provisioning/i,
|
||||
});
|
||||
await user.click(toggle);
|
||||
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
expect(onChange.mock.calls[0][0]).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { FormSection } from '@@/form-components/FormSection';
|
||||
import { SwitchField } from '@@/form-components/SwitchField';
|
||||
|
||||
interface AutoUserProvisionToggleProps extends Partial<AutomationTestingProps> {
|
||||
value: boolean;
|
||||
onChange: (value: boolean) => void;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export function AutoUserProvisionToggle({
|
||||
value,
|
||||
onChange,
|
||||
description,
|
||||
'data-cy': dataCy = 'auto-user-provision-toggle',
|
||||
}: AutoUserProvisionToggleProps) {
|
||||
return (
|
||||
<FormSection title="Automatic user provisioning">
|
||||
<div className="form-group">
|
||||
<span className="col-sm-12 text-muted small">{description}</span>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<div className="col-sm-12">
|
||||
<SwitchField
|
||||
label="Automatic user provisioning"
|
||||
checked={value}
|
||||
onChange={onChange}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</FormSection>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user