* [MM-63717] LDAP Wizard skeleton (#31029)

* add ldap_wizard component to render its admin components

* i18n

* test adjustment

* keys and props fixes

* title fix

* fix placeholders

* fix value initialization

* linting

* remove all ...props (except custom component); any->unknown

* fix i18n (temp, will be changed in later PR)

* better return; simplify function checking/calling

* [MM-64259] Sections sidebar and navigation (#31059)

* initial sections list sidebar

* sidebar highlighting and scroll on click

* some tidying up

* add custom section titles for section sidebar

* i18n

* updating border on sections

* scss style lint

* color -> border-color

* simplify activeSectionKey initialization; remove trailing newline

* add useSectionNavigation; clean up ldap_wizard and scss; PR comments

* extract section of code into renderSidebar()

---------

Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com>

* [MM-64296] Add test connection for connection settings panel (#31190)

* button -> ldap test connect api

* fix console error by sanitizing value in text component

* return detailed error as error; adjust button -> primary, flushLeft

* middle of redesigning how we do hover text, first button

* add hover text to bools and file uploads

* i18n

* add LdapSettings as api type; add new endpoint to api yaml

* allow testing without first enabling LDAP and saving config

* i18n id changes

* improve TestLdapConnection to current standards

* PR comments

* safeDereference; cleaner returns

* remove hover markdown; formatting and typing simplification

* use button for "More Info"; i18n

* finish renaming help_text_hover -> help_text_more_info

* fix error output

* only send bindpassword if it has been changed

* fix: don't send blank bindPassword when it is still *****

* merge conflict

* [MM-64480] Refactor Admin Definition (#31280)

* move ldap definition to its own file for simplicity & context

* refactor admin_definition to eliminate circular dependencies

* merge conflicts

* before: buggy userHasReadPermissinOnSomeResources; after: fix incorrect snapshot

* merge conflict: new bindPasssword definition was left behind; fixed.

* merge conflict

* [MM-63765] LDAP Wizard: User filter expandable section (#31286)

* add "more info" hover to user filter help texts; make wider

* add expandable_setting type and component

* use Dislosure show/hide pattern for accessibility

* fix tooltip scss selectors

* fix hover -> more_info; make sure translation files are correct

* use join('\n\n') instead of the eslint disable line

* Revert "use join('\n\n') instead of the eslint disable line"

This reverts commit 274667e875b34703f14fee0706cd28b0125cefc9.

* [MM-64482] LDAP Wizard - Test User filters (#31312)

* initial cut at UI and backend for test filters

* api definitions; mocks

* clean up to current standards

* [MM-64512] - Test user filters UI (#31355)

* result_count -> total_count

* json cannot marshal error, returning error as string as god intended

* render errors with icon, hover text, and better feedback texts

* gather the settings that may be in expandable sections

* remove success, use error == "" to indicate success

* [MM-64536] LDAP Wizard: Test user attributes (#31373)

* LdapFilterTestResult -> LdapDiagnosticResult; FilterName -> TestName

* implement test_attributes endpoint and limited frontend (first step)

* adding EntriesWithValue

* [MM-64550] LDAP Wizard: Test user attributes UI (#31374)

* [MM-64551] LDAP Wizard: Test group attributes (#31375)

* remove Test LDAP button (not needed); reused helptext for other btn

* implement test_group_attributes endpoint; button/client-side paths

* [MM-64552] LDAP Wizard: Test group attributes UI (#31376)

* implement Test Group Attributes button

* simplify helper functions (improves useCallback dependencies)

* show the default filter that was used on the backend in the tooltip

* show the icon when there's an error (e.g. required filter/attribute)

* fix infinite rerendering

* fix error after failed save; fix navigation unlocked after save

* empty

* Adjust message feedback given we don't test the schema anymore

* improve css; don't use inline styles

* removed unneccesary pointer indirection

* improved i18n strings and logic

* combining filters/attributes/group attributes endpoints

improve types

* improve help text for User Filter (it's tricky)

* AvailableAttrs -> AvailableAttributes

* fix for e2e tests (renamed title)

* more e2e fixes

* skip broken e2e test

---------

Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com>
Этот коммит содержится в:
Christopher Poile
2025-06-16 16:19:33 -04:00
коммит произвёл GitHub
родитель 4278296d45
Коммит 548a47ae56
43 изменённых файлов: 3763 добавлений и 1384 удалений

Просмотреть файл

@@ -23,6 +23,9 @@ type mixedUnlinkedGroup struct {
func (api *API) InitLdap() {
api.BaseRoutes.LDAP.Handle("/sync", api.APISessionRequired(syncLdap)).Methods(http.MethodPost)
api.BaseRoutes.LDAP.Handle("/test", api.APISessionRequired(testLdap)).Methods(http.MethodPost)
api.BaseRoutes.LDAP.Handle("/test_connection", api.APISessionRequired(testLdapConnection)).Methods(http.MethodPost)
api.BaseRoutes.LDAP.Handle("/test_diagnostics", api.APISessionRequired(testLdapDiagnostics)).Methods(http.MethodPost)
api.BaseRoutes.LDAP.Handle("/migrateid", api.APISessionRequired(migrateIDLdap)).Methods(http.MethodPost)
// GET /api/v4/ldap/groups?page=0&per_page=1000
@@ -45,7 +48,7 @@ func (api *API) InitLdap() {
func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.syncLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
c.Err = model.NewAppError("api4.syncLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
@@ -73,7 +76,7 @@ func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
func testLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.testLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
c.Err = model.NewAppError("api4.testLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
@@ -90,6 +93,71 @@ func testLdap(c *Context, w http.ResponseWriter, r *http.Request) {
ReturnStatusOK(w)
}
func testLdapConnection(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !model.SafeDereference(c.App.Channels().License().Features.LDAP) {
c.Err = model.NewAppError("api4.testLdapConnection", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionTestLdap) {
c.SetPermissionError(model.PermissionTestLdap)
return
}
var settings model.LdapSettings
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
c.SetInvalidParamWithErr("ldap_settings", err)
return
}
if err := c.App.TestLdapConnection(c.AppContext, settings); err != nil {
c.Err = err
return
}
ReturnStatusOK(w)
}
func testLdapDiagnostics(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.testLdapDiagnostics", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionTestLdap) {
c.SetPermissionError(model.PermissionTestLdap)
return
}
testTypeStr := r.URL.Query().Get("test")
if testTypeStr == "" {
c.SetInvalidParam("test")
return
}
testType := model.LdapDiagnosticTestType(testTypeStr)
if !testType.IsValid() {
c.SetInvalidParam("test")
return
}
var settings model.LdapSettings
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
c.SetInvalidParamWithErr("ldap_settings", err)
return
}
res, appErr := c.App.TestLdapDiagnostics(c.AppContext, testType, settings)
if appErr != nil {
c.Err = appErr
return
}
if err := json.NewEncoder(w).Encode(res); err != nil {
c.Logger.Warn("Error while writing response", mlog.Err(err))
}
}
func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementGroups) {
c.SetPermissionError(model.PermissionSysconsoleReadUserManagementGroups)
@@ -97,7 +165,7 @@ func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
}
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.getLdapGroups", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
c.Err = model.NewAppError("api4.getLdapGroups", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
@@ -135,7 +203,7 @@ func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
Groups []*mixedUnlinkedGroup `json:"groups"`
}{Count: total, Groups: mugs})
if err != nil {
c.Err = model.NewAppError("Api4.getLdapGroups", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
c.Err = model.NewAppError("api4.getLdapGroups", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
return
}
@@ -160,7 +228,7 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
audit.AddEventParameter(auditRec, "remote_id", c.Params.RemoteId)
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
c.Err = model.NewAppError("api4.linkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
@@ -171,7 +239,7 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
}
if ldapGroup == nil {
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_group.not_found", nil, "", http.StatusNotFound)
c.Err = model.NewAppError("api4.linkLdapGroup", "api.ldap_group.not_found", nil, "", http.StatusNotFound)
return
}
@@ -234,7 +302,7 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
b, err := json.Marshal(newOrUpdatedGroup)
if err != nil {
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
c.Err = model.NewAppError("api4.linkLdapGroup", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
return
}
@@ -262,7 +330,7 @@ func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
}
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.unlinkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
c.Err = model.NewAppError("api4.unlinkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}
@@ -305,7 +373,7 @@ func migrateIDLdap(c *Context, w http.ResponseWriter, r *http.Request) {
}
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.idMigrateLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
c.Err = model.NewAppError("api4.idMigrateLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
}