updateInterfaceLdapFilters saved the matched users with SaveInterface,
which creates the interface when it is missing.
On first start that panics. The sync runs immediately (main.go:85) and
the importer later (main.go:116), so the sync creates a stub row for
every interface_filter key, and the importer, which snapshotted the
interface list before its device round-trips, then fails with "interface
already exists". The window is GetInterfaces plus GetPeers, so a
directory on localhost loses the race and a slower one hides it.
The stub rows are wrong anyway. They have no backend, so a typo in an
interface_filter key quietly created an interface attached to no
controller.
Look the interface up and skip with a warning when it is absent. The
filter is applied on the next sync once the importer has created it. A
lookup error that is not ErrNotFound also skips.
Signed-off-by: clark-ja <37738506+clark-ja@users.noreply.github.com>
disable_missing tested absence against the raw sync result without
checking that the search returned anything usable, so a search that
succeeds and yields nothing looked like "every user has been removed".
Connection and search errors were already safe, since synchronizeLdapUsers
returns before the disable phase. The gap is the successful-but-empty
case: a base_dn or sync_filter that stops matching, an unpopulated
replica, a field_map user_identifier naming an attribute the server does
not return, or a bind account that lost read access to the user subtree.
LDAP gives nothing to tell those apart from a directory that is genuinely
empty; they all answer success with zero entries.
Acting on it is not a database flag. TopicUserDisabled removes each
user's peers from the WireGuard device, the successful search means no
error is logged, and every message on the path was Debug while log_level
defaults to info, so the whole event was silent. It also repeats every
sync interval.
Refuse to disable anyone when no usable identifier came back, logging the
provider, entry count and identifier field. The guard counts identifiers,
not entries, so it covers the field_map case too. The per-user disable
line moves from Debug to Warn so a mass disable is audible even where the
guard does not fire.
The cost is that a directory intentionally emptied of users now disables
nobody. That is documented, along with the workaround: leave one account
matching sync_filter and everyone else is disabled as before.
Signed-off-by: clark-ja <37738506+clark-ja@users.noreply.github.com>
NFC normalisation ran before control and format characters were stripped.
Removing a character can leave a base letter next to a combining mark the
earlier pass never saw as a pair, so a second call composes it:
input U+0041 U+0009 U+0300 ("A", tab, combining grave)
once -> U+0041 U+0300
twice -> U+00C0
Found by TestPropertySanitizeStringIdempotent. Category Cf characters
behave the same way.
Strip first, then normalise. Normalisation still precedes truncation
because composing changes the rune count. One side effect: invalid UTF-8
is now dropped by the strip loop instead of surviving as U+FFFD, so an
identifier containing such bytes sanitises differently than before.
Signed-off-by: clark-ja <37738506+clark-ja@users.noreply.github.com>
* Add dynamic Mikrotik peer support & UI/i18n
Introduce handling for dynamically created Mikrotik peers: add Dynamic/IsDynamic fields to domain, peer and interface models and API models; include "dynamic" in Mikrotik queries; skip updates/deletes for dynamic peers; sync dynamic flag when restoring state. Prevent modifying/deleting dynamic peers in manager. UI: disable selection/edit for dynamic peers and show badge. Implement Mikrotik interface hook execution. Add i18n keys/translations for dynamic-peer messages across languages.
* Fix Mikrotik hooks implementation and UI visibility
* fix: build OAuth return URL from the live location, not the Vite base
On base_path deployments, OAuth/OIDC login failed with a 400 "invalid
return URL" before the user ever reached the IdP.
LoginView and the router derived the app's runtime URL mount from
import.meta.env.BASE_URL, which is Vite's build-time *asset* base. Since
#711 set `base: './'` to fix relative asset loading (#710), BASE_URL is
"./", so `base_path: /wg` produced the return URL https://host/wg./#/login.
isValidReturnUrl() requires <base_path>/app, so every external-auth user
on a base_path deployment was locked out.
Derive the return URL from window.location instead: the app is mounted at
{base_path}/app/ in production and at / under `npm run dev`, so the live
document location is the only reliable source. Likewise drop the explicit
base from createWebHashHistory(), which then defaults to
`location.pathname + location.search` -- correct in every deployment.
This leaves the relative asset base from #711 untouched, so #710 stays
fixed, and it removes the last two readers of import.meta.env.BASE_URL
under frontend/src so the asset base can no longer affect routing.
Fixes#719
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dan Berg <dan@webinargeek.com>
* fix: log rejected OAuth return URLs
The isValidReturnUrl() rejection was the only failure branch in
handleOauthInitiateGet without a slog call, and the 400 response body
reveals neither what was received nor what was expected. That is what
made #719 hard to diagnose.
Log both at Debug level, matching the neighbouring branches. The response
body is unchanged -- the URL is not echoed to the client.
Also note on the base-path test that the URL shape it pins is produced by
externalLogin() in frontend/src/views/LoginView.vue, so a future frontend
change has a breadcrumb back to the contract.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dan Berg <dan@webinargeek.com>
---------
Signed-off-by: Dan Berg <dan@webinargeek.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: resolve Prism syntax highlighting error in peer config modal
The PeerViewModal and InterfaceViewModal components displayed:
Error: The language "undefined" has no grammar.
Root cause: vue-prism-component bundles its own instance of prismjs
(separate from the one imported elsewhere), so registering the ini
grammar on a different Prism instance had no effect on the one
actually performing the highlighting.
Fix: bypass vue-prism-component entirely. Import a single Prism
instance via a new helper (frontend/src/helpers/prism-setup.js) that
also registers the ini grammar, then render highlighted HTML directly
with <pre><code v-html="highlightedConfig">. This ensures the same
Prism object that owns the ini grammar is the one calling highlight().
Affected files:
- frontend/src/helpers/prism-setup.js (new): imports prismjs, defines
the ini grammar on the Prism instance, re-exports it
- frontend/src/components/PeerViewModal.vue: replaces <Prism> with
v-html using the shared Prism instance
- frontend/src/components/InterfaceViewModal.vue: same replacement
- frontend/src/main.js: removes the now-unused prism-setup import
Fixes#726
* Revert "fix: resolve Prism syntax highlighting error in peer config modal"
This reverts commit 8d1155224d.
* fix: resolve Prism syntax highlighting error in peer config modal
The PeerViewModal displayed: Error: The language "undefined" has no grammar.
Root cause: Vite bundled prismjs as separate instances per lazy-loaded
chunk, so the ini grammar registered in one instance was invisible to
vue-prism-component which used a different instance.
Fix:
- Add dedupe: ['prismjs'] to vite.config.mjs to force a single Prism
instance across all chunks
- Add the missing 'prismjs/components/prism-ini' import in
PeerViewModal.vue (InterfaceViewModal already had it)
Remove hardcoded `--base=/app/` from npm build script and set `base:
'./'` in vite.config to generate relative asset URLs
Update server `updateBasePathInFrontend` to handle relative paths
Fixes#710
Signed-off-by: Rich C <richcarni@gmail.com>
* Return empty string instead of "<nil>" when a genericjsonobject key doesn't exist.
* Fix pfsense backend
* Fix API request parameter names and types
* Refactor interface and peer creation to send the necessary parameters
* Automatically call apply when interfaces or peers are changed
Signed-off-by: Aram Akhavan <1147328+kaysond@users.noreply.github.com>
---------
Signed-off-by: Aram Akhavan <1147328+kaysond@users.noreply.github.com>
The OIDC client was only extracting claims from the ID token, but many
OIDC providers (like Authelia) don't include all user information in the
ID token. Fields like 'preferred_username' are typically only available
via the userinfo endpoint.
This fix fetches additional user information from the provider's userinfo
endpoint and merges it with the ID token claims, ensuring that all
required user fields are available for user registration and login.
Fixes#697
Signed-off-by: Aram Akhavan <1147328+kaysond@users.noreply.github.com>
By default, the systemd-networkd.service(8) removes routing policy
created by other tools when it starts. This can cause wireguard tunnels
to stop working during a system upgrade or other administration
actions. Document the configuration necessary to prevent this occuring.
Signed-off-by: Mark Lawrence <mark@rekudos.net>
- Add frontend/src/lang/translations/ja.json (full translation of all 422 entries from en.json)
- Register ja in frontend/src/lang/index.js so it appears in the language selector
Tested locally with v2.2.3 build. UI strings render correctly in Japanese.
Closes: language support request
Co-authored-by: Taro Kawakami <tarokun@tunagufactory.jp>
* Add test-in-docker target to Makefile
Add a target to run tests in Docker for non-Linux environments.
* Add GOVERSION variable to Makefile
* fix: update test-in-docker command to use user permissions
* Fix docker command syntax in Makefile
* feat(frontend): add confirmation dialog before deleting users, peers, and interfaces (#652)
Add a browser confirm() dialog to the delete functions in UserEditModal,
PeerEditModal, and InterfaceEditModal to prevent accidental deletions.
The bulk-delete actions in UserView already had this protection; this
change brings single-item deletion in line with that behavior.
Translation keys (confirm-delete) added for all 10 supported locales:
de, en, es, fr, ko, pt, ru, uk, vi, zh.
Signed-off-by: LeC-D <leo.openc@gmail.com>
* fix broken translation files
---------
Signed-off-by: LeC-D <leo.openc@gmail.com>
Co-authored-by: Christoph Haas <christoph.h@sprinternet.at>