Commit graph

45 commits

Author SHA1 Message Date
a9c2f8e5e5 fix/findings-medium-priority (#188)
Reviewed-on: graham/towerops-web#188
2026-03-27 12:31:53 -05:00
1590f78bdc fix: input validation, SSRF, API hardening, and cookie security
- LIKE wildcard injection: sanitize %, _ in search queries (devices, sites, gaiia)
- Jason.decode! → Jason.decode with error handling for untrusted input
- inspect() leak: replace with generic error messages, log details server-side
- SSRF protection: URL validator blocks private IPs, localhost, non-HTTP schemes
- SSRF validation added to HTTP monitoring executor and integration credentials
- GraphQL complexity limits: always applied, not just in prod
- GraphQL introspection: also check GET query params, not just body
- Stripe webhook: explicit nil/empty checks for signature and body
- Cookie security: secure flag for session (prod), http_only+secure for remember_me
- Honeybadger API key: read from env var with fallback
- String.to_integer → Integer.parse with fallback for URL params
- String.to_atom → whitelist map for HTTP methods
- Gaiia webhook: remove secret_len and expected signature from log
- Admin API: add rate limiting pipeline
- to_atom_keys: per-key fallback instead of all-or-nothing rescue
2026-03-14 14:48:59 -05:00
529a6138d1
fix: align sidebar bottom border with footer border
Adjust sidebar bottom section padding from py-3 to py-3.5 to align
the horizontal border with the footer border. Also remove unused
private functions from settings_live and fix credo warnings.
2026-03-13 16:29:44 -05:00
88323a2290 Deduplicate integrations: settings tab links to standalone page
- Replace inline integrations tab with link to /settings/integrations
- Remove ~1500 lines of duplicated integration UI from settings
- Remove duplicated integration event handlers from settings_live
- Standalone IntegrationsLive page remains the single source of truth
- Settings page down from ~2400 to ~900 lines
2026-03-13 16:16:35 -05:00
37314bd154 Add alert routing setting (builtin/pagerduty/both)
- New 'alert_routing' field on organizations (default: builtin)
- AlertNotificationWorker respects the setting:
  - builtin: only built-in escalation policies
  - pagerduty: only PagerDuty events
  - both: fire both (useful during migration)
- Radio button UI in Settings → General with visual cards
- Warning shown when PagerDuty selected but not configured
- Migration, schema validation, 6 tests
2026-03-13 15:26:25 -05:00
bbd74e895f
fix: prevent credential inputs resetting on re-render for all integrations
All non-NetBox provider forms (Sonar, Splynx, Gaiia, Preseem, PagerDuty,
VISP) had the same bug as NetBox: credential inputs used value= bindings
reading from saved DB state. Any phx-click event (Test Connection, etc)
triggering a re-render would wipe unsaved typed values.

Fix by adding @integration_credentials assign, initialized from saved
integration when opening config and updated from form params on every
phx-change via validate_integration.
2026-03-12 14:31:31 -05:00
f980e9eb6d
fix: preserve NetBox url/api_token in netbox_config across re-renders
Sync direction button clicks (phx-click) triggered a LiveView re-render
that reset the url and api_token inputs to their saved DB values, wiping
any unsaved typed text. Fix by tracking url and api_token in @netbox_config
so they survive click-triggered re-renders.
2026-03-12 14:25:08 -05:00
58181923f8
improve Gaiia integration UX
- Make insight messages more descriptive (explain what untracked/ghost/mismatch means)
- Clarify action button labels on insight cards
- Redesign Gaiia mapping page to be TowerOps-centric: devices/sites as primary rows, search Gaiia for what to link them to
- Add IP-based match suggestions and smart sorting (suggestions first, then unlinked, then linked)
- Add search_inventory_items/2 and search_network_sites/2 to Gaiia context
- Make IP addresses clickable links (http://<ip>)
- Add optional Gaiia App URL setting to enable deep links to inventory items and network sites
2026-03-12 13:04:17 -05:00
031427a247
feat: collapse and dim inactive billing integrations
When a billing provider is active (enabled), the other billing providers
are dimmed and collapsed to a single row showing a lock icon and which
provider is active. This reinforces the mutually exclusive constraint.

Also fixes status emoji accumulating in page title (JS regex needed
unicode flag) and adds missing page_title to 8 LiveViews.
2026-03-11 10:00:38 -05:00
f2608e8f16
fix: status emoji accumulating in page title and add missing page titles
Fix JS regex in StatusTitle hook to use unicode flag so emoji are
properly matched and replaced instead of accumulating. Add page_title
to 8 LiveViews that were missing it, which caused "TowerOps | TowerOps"
duplication from the live_title suffix.
2026-03-11 09:54:18 -05:00
198f2c3cbc
feat: add API key setup help to integration configuration panels
Add setup_help info boxes to each integration provider showing where
to create API keys, step-by-step instructions, required permissions,
and example URL patterns.
2026-03-11 09:46:22 -05:00
3fa0ea383a
feat: improve organization invitation UX
- Show sender's full name in invitation emails
- Use relative time for "Sent" column (more readable)
- Use date format for "Expires" column (shorter)
- Make invitation table horizontally scrollable

Fixes display cropping issue where long timestamps caused
the pending invitations table to overflow its container.
2026-03-07 11:58:46 -06:00
4b4bb2668f
feat: add billing tab to organization settings
Implements Phase 6 of Stripe billing integration:
- Add Billing tab to organization settings page
- Display subscription status, device usage, and estimated costs
- "Upgrade to Paid Plan" button for free tier orgs
- "Manage Billing" button to access Stripe Customer Portal
- Billing information panel showing subscription details
- Support for dark mode and responsive design

Also fixes organization name truncation in top navigation:
- Long organization names now show with ellipsis instead of being cut off
- Added max-w-xs constraint and truncate class to org switcher button
2026-03-06 11:08:05 -06:00
0cd62951ec Replace 'Other Integrations' with specific category headers on settings page
Each non-billing integration now shows under its proper category:
- Quality of Experience (Preseem)
- Incident Management (PagerDuty)
- Infrastructure & IPAM (NetBox)
2026-02-16 10:06:47 -06:00
541b418d57 Group billing providers on settings integrations tab
Add 'Billing & Subscriber Management' section header with 'Choose one'
badge and 'Other Integrations' divider on the settings page integrations
tab. Reorder providers: billing first (Gaiia, Sonar, Splynx, VISP),
then QoE/incidents/infrastructure.
2026-02-16 08:34:46 -06:00
b4c0407ee0 Add granular org roles: executive, technician
New role system:
- owner: full access + financials
- admin: full access + financials (except org deletion)
- executive: read-only + full financial visibility (MRR, revenue)
- technician: field ops (devices, sites, alerts) without financials
- member: legacy alias for technician (migrated)
- viewer: read-only, no financials

Financial data (MRR, revenue, billing) gated behind can_view_financials
assign in all LiveView templates: dashboard, alerts, trace, site show.

Includes migration to rename existing member roles to technician.
2026-02-15 17:40:53 -06:00
fbcf26656e
fix compile warning 2026-02-15 12:38:27 -06:00
c790794191 Fix tests, credo issues, and Gaiia sync bugs
- Fix CLOAK_KEY placeholder in nix shell (invalid base64)
- Fix error HTML test assertion to match actual template
- Fix Gaiia sync: read 'subnet' field instead of 'block' for IP blocks
- Fix Gaiia sync: extract nested status name from subscription objects
- Fix security headers tests for environment detection
- Fix mobile auth tests to use raw_token
- Fix LiveView test assertions to match actual template content
- Fix SNMP config test expectations for test environment
- Refactor: resolve all Credo complexity/nesting issues
  - Extract helper functions in NetBox sync, VISP sync, Sonar sync
  - Flatten nested conditionals in settings, integrations, alerts
  - Use with/validate_present pattern for connection testing
2026-02-15 11:55:49 -06:00
66bf61887f feat: add Sonar, Splynx, and VISP billing integrations 2026-02-14 20:18:57 -06:00
02474c529f ui: major redesign - NOC dashboard, consolidated nav, dense tables, flat alerts 2026-02-14 20:15:07 -06:00
8752dfec49 fix: netbox url field type, gaiia ipRange→block, remove unknown webhook log 2026-02-14 17:44:01 -06:00
243b773e39 i18n: wrap all user-facing strings in gettext() 2026-02-14 17:44:01 -06:00
712174cc47 Fix NetBox test connection: read URL/token from form, not just saved state 2026-02-14 16:15:59 -06:00
7b72ed3164 PagerDuty two-way sync: webhook receiver for resolve/acknowledge
- POST /api/v1/webhooks/pagerduty/:organization_id endpoint
- HMAC-SHA256 signature verification (optional, via webhook_secret)
- incident.resolved → resolve_alert_silent (no re-notify to PagerDuty)
- incident.acknowledged → acknowledge_alert_silent (no re-notify)
- Matches alerts via dedup_key format: towerops-alert-<uuid>
- Webhook secret configurable in org settings PagerDuty panel
- Setup instructions in UI for PagerDuty Generic Webhooks (v3)
2026-02-14 14:58:58 -06:00
44e9d20d35 fix: NetBox sync direction cards now clickable via phx-click instead of hidden radio buttons 2026-02-14 14:41:30 -06:00
47f9ee703f fix: NetBox sync direction/checkboxes now reactive - track config in socket assigns instead of reading from saved credentials 2026-02-14 14:30:46 -06:00
5917190efe feat: add NetBox integration with sync direction, entity selection, and filtering
- Three sync modes: NetBox→TowerOps (pull), TowerOps→NetBox (push), Bidirectional
- Configurable entity sync: devices, sites, IP addresses, interfaces
- Optional filters by device role, site, and tag (comma-separated)
- NetBox client with test connection, CRUD operations
- Radio card UI for sync direction, checkbox cards for entity selection
- Credentials stored encrypted (URL, API token, sync config)
2026-02-14 14:09:01 -06:00
ea7d82a489 fix: make Configure button toggle - clicking again collapses the form 2026-02-14 13:48:22 -06:00
9b5647a055 fix: Gaiia webhook secret is user-pasted from Gaiia, not auto-generated; update setup instructions 2026-02-14 13:34:32 -06:00
146f5745cf
fix: resolve compilation errors, test failures, and credo issues
- Escape HEEx template braces in GraphQL/API docs with raw(~S[...])
- Fix test assertions for updated marketing copy and UI text
- Extract helper functions in GraphQL resolvers to reduce nesting depth
- Create shared ErrorHelpers module for API controllers
- Fix ETS race condition in brute force whitelist cache for async tests
- Fix property test generators to use ASCII instead of printable unicode
- Add alert_severity helper to site_live/show
- Update accounts fixtures for explicit user confirmation
2026-02-14 12:23:10 -06:00
bb4ff232e3 feat: add PagerDuty Events API v2 integration with 2-way alert sync 2026-02-14 11:28:57 -06:00
f326eb5dd4 feat: require email verification before first login
- Remove auto-confirmation on registration (register_user and register_user_with_organization)
- Add deliver_user_confirmation_instructions/2 and confirm_user/1 to Accounts context
- Add verify_email_token_query/2 and by_user_and_contexts_query/2 to UserToken
- Make deliver_confirmation_email public in UserNotifier
- Send confirmation email after registration in both flows
- Block unconfirmed users at password login with helpful message
- Add UserConfirmationController with confirm/resend routes
- Add resend confirmation link to login page
- Update test fixtures to confirm users after registration
2026-02-14 11:28:57 -06:00
95ce9ad06d refactor: org settings into tabbed interface with integrations 2026-02-14 11:28:57 -06:00
fc66109f19
Add default organization feature
Features:
- Users can set a default organization in org settings
- First organization created is automatically set as default
- Default org selector only shown if user has 2+ organizations
- Migration auto-sets first org as default for existing users

Database changes:
- Added is_default boolean field to organization_memberships
- Added partial index for efficient default org lookups
- Migration sets oldest org as default for each existing user

UI changes:
- Organization settings page shows default org toggle
- Green badge displayed when org is already default
- Button to set org as default if it's not current default

Backend:
- Organizations.set_default_organization/2 manages defaults
- Organizations.get_default_membership/1 retrieves default
- Auto-set first org as default in create_organization/3
2026-02-04 16:09:43 -06:00
02413d5cfd
cleanup forms 2026-02-02 10:07:37 -06:00
f938b263cd
Mikrotik backup and gettext start 2026-02-02 09:11:22 -06:00
a48cb292da
update org settings layout 2026-02-01 17:42:14 -06:00
6ca22c5dd0 enhance user auditing 2026-01-30 17:38:07 -06:00
8e739283ad refactoring 2026-01-29 10:03:43 -06:00
541ab7e7d6
more renames 2026-01-17 15:13:56 -06:00
a810e75fc4
rename equipment to device 2026-01-17 14:48:46 -06:00
f9a18fa423
Fix clipboard copy and org settings navigation
- Replace phx:copy event with CopyToClipboard LiveView hook for more
  reliable clipboard functionality
- Add visual feedback (checkmark) when text is copied to clipboard
- Apply hook to both agent token and docker compose copy buttons
- Add readonly attribute to docker compose textarea
- Fix org settings page to stay on settings after save instead of
  redirecting to dashboard

The clipboard copy now works correctly by using a dedicated LiveView
hook that directly handles the click event and clipboard API, rather
than relying on custom event dispatching which was causing issues.
2026-01-17 12:18:49 -06:00
dca90db137
allow override of agent and snmp community 2026-01-17 12:05:08 -06:00
8864b8d26d
Enhance agent assignment UI with clear inheritance indicators
- Add override warnings (amber) when site/equipment explicitly override defaults
- Show inherited agent names in organization and site forms
- Display equipment assignment breakdown in organization settings
- Make equipment boxes fully clickable on site page (remove View button)
- Add hover effects to equipment boxes for better UX
- Consistent icon usage across all assignment levels
2026-01-14 16:20:20 -06:00
cf77949527
add remote agent setup 2026-01-09 13:15:31 -06:00