Users settings page (replaces standalone users app)#750
Open
cyberb wants to merge 38 commits into
Open
Conversation
… + authelia mail claim + dev stub
…, /home/<user>, loginShell, givenName)
…p chips (syncloud hidden), last-admin lock
…roup create/assign
…e, backend+stub reject blank) + e2e
… jest unit tests for users list/edit screens
…te confirmation dialog (+ e2e/jest)
… from anchor styling)
…red endpoints Mirror the backend's per-endpoint admin checks in the UI: routes whose data comes only from AdminSecuredHandle endpoints (access, internalmemory, storage, updates, backup, certificate, certificate/log, health) now carry meta.admin and their Settings tiles render only for admins. Pages served via SecuredHandle (network, support, logs, customproxy, system) and the mixed read-secured/write-admin pages (activation, twofactor, locale) stay visible to regular users.
Jest Settings.spec asserts the tile gating both ways (non-admin sees locale/twofactor and the user-facing tiles, never the admin tiles; admin sees all). Playwright 11-nonadmin-settings creates a non-admin user via the platform CLI, logs in, and confirms locale and two-factor open while admin tiles (storage, users) are absent.
The users app being replaced binds as the rootDN (dc=syncloud,dc=org) for every operation. Collapse adminBind into rootBind so all writes use one identity, matching that model. Removes the cn=admin path entirely, including the pre-existing inline binds in IsAdmin and RemoveUser. No ACL migration is needed since the rootDN bypasses ACLs on every existing device.
… user and group into own types Move the User and Group structs, password complexity check, email default/ validation, and the users-app LDAP attribute map out of ldap.go into dedicated type files with constructors, each with its own unit test that exercises the type directly. Inject the three collaborators into the ldap Service via ioc instead of package-level funcs. AddUser now takes the admin flag and sets group membership in the same bind, so the UserAdd handler makes a single service call instead of orchestrating add-then-set-admin. REST request bodies for the user/group endpoints become named types, one per file, instead of inline anonymous structs. cli user add gains --admin.
Add meta.admin to these four routes and hide their entry points from regular users: the customproxy and system Settings tiles, and the App Center nav links (header, mobile menu, empty-apps link). System already holds reboot/ shutdown, so gating the page hides those buttons from non-admins. Protect the backend calls behind them to match: restart, shutdown and proxy_custom list/add/remove move from SecuredHandle to AdminSecuredHandle. apps/available and app install/remove/upgrade were already AdminSecured; activate stays FailIfActivated (runs pre-activation, before any admin exists). Jest Settings.spec moves customproxy/system into the admin-tile set; the non-admin Playwright spec asserts the tiles and App Center link are hidden.
Regular users only need two-factor, so gate the remaining user-facing settings behind admin: activation, network, support, logs and locale now carry meta.admin and their Settings tiles render only for admins. Protect their backend calls to match: network/interfaces, logs, logs/send, device/url and the timezone/time GETs move from SecuredHandle to AdminSecuredHandle. What stays open to any logged-in user is exactly the non-settings surface plus 2FA: /rest/user, apps/installed, app, proxy/image and settings/2fa GET. Settings.spec keeps only two-factor in the user-tile set; the non-admin Playwright spec asserts every other tile is hidden and only two-factor opens.
Per-user OTP enrollment lives in the separate login app (web/login LoginApp.vue totp_setup, served by the login service), not platform settings; the platform two-factor page is only a device-wide admin enable/disable toggle. So a regular user needs nothing under settings. Gate the whole area instead of per-tile: /settings and /twofactor carry meta.admin, the Settings nav links (header + mobile) render only for admins, the two-factor tile and the settings/2fa GET become admin-only. Platform login stays open since it is the shared Authelia SSO every app uses; a regular user simply lands on their apps with no settings or app-center navigation. Settings.spec asserts non-admins see no tiles and admins see all; the non-admin Playwright spec asserts apps are visible but settings and app-center navigation are absent.
… link The #apps nav link is display:none on mobile (collapsed into the burger), so toBeVisible failed on the mobile project. Assert the Applications heading (viewport-agnostic, as other specs do) and check both desktop and mobile admin nav links are absent.
…ap.AddRequest Instead of returning an attribute map the caller has to assemble into an add request (and tack the password hash on), UserBuilder.Build takes all the params it needs (username, email, uid, password) and returns a complete *ldap.AddRequest. AddUser just executes it with conn.Add.
…dHasher Drop the member bool that toggled add vs remove. SetGroupMember/modifyMember become AddGroupMember/RemoveGroupMember (+ private addMember/removeMember), the REST endpoint splits into /rest/groups/member/add and /rest/groups/member/remove with a shared GroupMemberRequest, and the UI/stub call the matching one. Each handler now makes a single unambiguous service call. Also extract makeSecret into a PasswordHasher type with a Hash method, injected into the ldap Service (SetPassword, Reset) and into UserBuilder (the add request's userPassword), with its own unit test.
…ap Service Move the root dial+bind out of the Service's rootBind helper into an LdapClient type with Connect (dial + root bind) and Disconnect (close), injected via ioc. Every connection the Service opens now goes through s.ldapClient.Connect and is closed via s.ldapClient.Disconnect, so connection lifecycle lives in one place.
The Service did user CRUD, group CRUD, membership and LDAP lifecycle all at once. Split the CRUD out into two focused types sharing the injected LdapClient: - GroupManager (independent): group list/add/remove, membership add/remove, and Members(). Depends only on LdapClient. - UserManager (depends on GroupManager): user CRUD, SetAdmin, ListUsers and IsAdmin -- all of which read or write the syncloud admin group, so admin/group membership goes through GroupManager rather than duplicating member logic. Service keeps only what is neither user nor group CRUD: Init/ApplyConfig/Reset lifecycle plus Authenticate/AuthenticateUser credential checks. REST handlers now call userManager / groupManager independently; the admin middleware checker and cli user command resolve UserManager; login/activation keep using Service. ioc wires the three types; full-container resolution is covered by the existing public_api ioc test.
The leftover Service did two unrelated things: LDAP provisioning lifecycle (Init/ApplyConfig/Reset) and a single credential check used by the cli login command. Extract Authenticate into its own Authenticator type (the only consumer, cmd/cli/login, now resolves it), and rename the remaining lifecycle type to Initializer to say what it actually is. The unused zap logger field drops out with Authenticate.
…outes
These are plain backend endpoints, not a settings resource, so /rest/settings/2fa
becomes /rest/2fa and likewise for timezone, time and health/{events,metrics}.
Frontend callers and stub updated to match. Also remove the stale totp/setup
comment.
…tic error asserts Match the file and test file names to the Initializer type and give it an i receiver (the retry-loop counters become attempt to free up i). Switch the auth unit tests from assert.Nil/NotNil on errors to assert.NoError/Error so failures print the actual error.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a built-in Users settings page to the platform UI, replacing the need for the standalone
userssnap app (ldap-user-manager). Everything is managed directly against the platform's OpenLDAP via the Go backend.Features:
syncloudposix group (the platform's admin group). Guards against removing the last admin.<username>@<device-domain>, or a custom address that must be a valid email. Editable per existing user.syncloudadmin group is managed via the dedicated admin switch and excluded from the group list).mail: mailin the Authelia LDAP attribute mapping so apps that require theemailclaim can log in (previously the claim was never emitted).Backend (
backend/)auth/ldap.go: newListUsers,SetUserEmail,ListGroups,AddGroup,RemoveGroup,SetGroupMember,SetAdmin,ResolveEmail(default/validate, never empty).AddUsernow takes an email.DomainProviderinjected for the default-domain.rest/backend.go:/rest/users,/rest/users/{add,remove,email,admin},/rest/groups,/rest/groups/{add,remove,member}— all admin-secured.ioc, CLIuser add --emailupdated. Unit tests for email resolution.Frontend (
web/platform/)views/Users.vue, route, Settings tile, i18n keys across all 10 locales.src/stub/api.js) implements the new endpoints so the page works without a backend (npm run dev).Tests
auth/rest/iocpass. NewResolveEmailunit tests.specs/10-users.spec.ts— add user (default email), edit email, admin switch, group create + membership, removal.Notes
usersapp is intentionally left untouched; decommissioning it in the store is a separate effort.