Commit graph

372 commits

Author SHA1 Message Date
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
7a1317d0d0
API: Default organization_id to authenticated org when empty
When creating devices via API, organization_id is now properly defaulted
to the authenticated organization's ID even when an empty string is sent.

This fixes Terraform provider compatibility where organization_id may be
sent as "" instead of omitted entirely.

Before: Only checked if key was missing (Map.has_key?)
After: Also handles nil and empty string values
2026-02-04 16:03:20 -06:00
8382b5df6a
make devices site optional and many test fix 2026-02-04 15:05:42 -06:00
1a054fd598
make sites optional 2026-02-04 13:05:32 -06:00
4ef4f4fbf6
handle exceptions gracefully on api endpoints 2026-02-04 12:18:14 -06:00
09f2907ede
Remove debugging logs, fix Mix.env call 2026-02-04 12:10:48 -06:00
c93144cc37
snmp v3 support 2026-02-04 12:02:38 -06:00
3583624e0d
fix: prevent nil boolean error in device edit page
Fixed production bug where visiting the edit page for a device with no
SNMP discovery data would crash with "expected boolean on left-side of
'and', got: nil" error.

The mikrotik_device?/1 function was using && operator which returns nil
when device.snmp_device is nil, instead of returning false. The template
conditional on line 455 requires a proper boolean value.

Changed from:
  device.snmp_device && (...)

To:
  not is_nil(device.snmp_device) and (...)

This ensures the function always returns a boolean (true/false) instead
of potentially returning nil.

Added regression test to verify the page renders correctly when a device
has no SNMP discovery data yet.

Fixes: https://app.honeybadger.io/projects/136860/faults/127120431
2026-02-03 15:32:51 -06:00
ecf6edaf85
update docker compose instructions 2026-02-03 13:00:51 -06:00
c198fdeaa1
add snmp port 2026-02-03 12:51:50 -06:00
88d549cd92
more tests 2026-02-03 12:03:54 -06:00
5b84844e97
doc update 2026-02-03 09:07:06 -06:00
e181c17f78
fix: resolve MikroTik sensor values and empty SNMP communities
Fixes three critical issues affecting agent-based SNMP polling:

1. MikroTik gauge sensors incorrectly showing 10x values (223°C instead of 22.3°C)
   - Root cause: get_int_value() rejected zero values, causing mtxrGaugeUnit
     to be treated as nil and bypassing divisor logic
   - Fixed: Accept zero values and apply correct divisor=10 for temp/voltage/current/power

2. Empty SNMP community strings sent to agents causing authentication failures
   - Root cause: Devices with source="device" but null community weren't falling
     back to organization/site inheritance
   - Fixed: Enhanced resolve_snmp_community() to fall back to inheritance chain
     even when source="device" but community is empty

3. Continuous MikroTik API commands sent every 60s instead of once per 24h
   - Root cause: MikroTik jobs built on every poll cycle, not just discovery
   - Fixed: Only build MikroTik jobs during discovery phase

Migrations update existing data to fix incorrect divisor values and community
source tracking. Added comprehensive debug logging to diagnose future issues.
2026-02-02 17:12:36 -06:00
aaf8c38b78
handle mikrotik ssh 2026-02-02 16:42:18 -06:00
9a60bfec58
feat: migrate user settings and agent management to gettext
Migrates all remaining user settings and agent management flash messages to gettext:

User Settings:
- session_manager.ex: mobile device removal, alert preferences, browser session revocation, revoke all sessions
- totp_manager.ex: device creation, verification, deletion, recovery code generation
- api_token_manager.ex: token creation, deletion, validation errors
- user_auth.ex: policy consent acceptance message

Agent Management:
- agent_live/edit.ex: agent update success
- agent_live/index.ex: agent/cloud poller creation, token regeneration, agent deletion, global default settings

All flash messages now use domain-specific helpers (t_auth, t_equipment) for proper internationalization.

Tests passing: 4165 tests, 0 failures
2026-02-02 13:20:54 -06:00
3633ff1626
more gettext 2026-02-02 13:10:08 -06:00
0d85e85884
feat: migrate all remaining authentication and session flash messages to gettext
Completes the flash message migration by converting all remaining user-facing strings:

**UserAuth.ex changes:**
- All Plug functions: require_sudo_mode, require_authenticated_user, require_superuser
- load_current_organization error messages (3 messages)
- All LiveView on_mount callbacks (8 auth-related messages)
- Policy consent success message

**SessionManager.ex changes:**
- Mobile device session management (2 messages)
- Alert preferences updates (3 messages including conditional)

All messages now use t_auth() for consistent translation in the auth domain.

All tests passing (100 tests).
2026-02-02 13:02:44 -06:00
005235a54a
more gettext 2026-02-02 13:00:27 -06:00
2ef154e4d7
feat: migrate core components to gettext
Migrates consent prompt component strings to gettext for internationalization:
- Updated Policies modal title
- Policy review message
- Policy acceptance checkbox labels (Privacy Policy, Terms of Service)
- Consent disclaimer text
- Accept button text

All strings now use gettext() for translation support.
2026-02-02 12:49:13 -06:00
a995e6283f
feat: migrate admin features to gettext
Migrates all admin-related flash messages to gettext for internationalization:
- Admin.UserLive.Index: user deletion messages
- Admin.OrgLive.Index: organization deletion messages
- UserAuth: impersonation start/stop messages

Adds Gettext backend and helpers import to UserAuth module to support translations.

All admin and auth tests passing (87 tests).
2026-02-02 12:45:38 -06:00
89a076fb67
feat: migrate equipment features to gettext and fix sudo mode tests
Migrates all equipment-related flash messages to gettext for internationalization:
- DeviceLive.Index: device discovery, reordering, error messages
- DeviceLive.Show: backup messages, permission errors
- DeviceLive.Form: device CRUD operations
- AlertLive.Index: alert acknowledgment messages
- SiteLive.Show: site discovery messages
- SiteLive.Form: site CRUD and SNMP/agent propagation

Fixes sudo mode test suite issues:
- Updates Accounts.sudo_mode?/2 test to check last_sudo_at instead of authenticated_at
- Adds register_and_log_in_user_with_sudo/1 helper to ConnCase
- Fixes UserAuth sudo mode tests to use session-based authentication
- Updates MyData tests to use sudo mode (now required per router config)
- Removes obsolete UserSettingsLive "without sudo mode" test
- Fixes grant_sudo_mode/1 DateTime truncation to seconds

All equipment module tests passing. Ready for Phase 3 (Admin Features).
2026-02-02 12:41:58 -06:00
f6195cd5c6
sudo redirect fix 2026-02-02 10:54:32 -06:00
949cfaf4e0
add default org 2026-02-02 10:45:37 -06:00
1ba55ce451
mikrotik api update 2026-02-02 10:35:44 -06:00
bd6dad85e1
purge more passkey and gettext updates 2026-02-02 10:20:59 -06:00
02413d5cfd
cleanup forms 2026-02-02 10:07:37 -06:00
5d5116681a
device edit refactor 2026-02-02 10:00:00 -06:00
19b635c46f
feat: migrate email templates to gettext (Phase 2 - Emails)
- Update UserNotifier to use t_email() helper for all email content
- Extract 4 email subjects and 4 email bodies to emails domain:
  * Update email instructions
  * Log in instructions
  * Confirmation instructions
  * Reset password instructions
- Replace string interpolation with Gettext bindings (%{email}, %{url})
- Auto-populate English translations (msgstr = msgid)
- All emails now ready for future internationalization

First module migration complete per design plan.
2026-02-02 09:48:30 -06:00
cfb60cd186
feat: implement gettext infrastructure (Phase 1)
- Create domain-specific .pot template files (auth, equipment, admin, emails)
- Create corresponding English .po files for all domains
- Add GettextHelpers module with domain-specific helper functions:
  * t() for default domain (common UI)
  * t_auth() for authentication flows
  * t_equipment() for equipment/device management
  * t_admin() for admin features
  * t_email() for email templates
- Import GettextHelpers in html_helpers() for automatic availability in LiveViews/components
- Add mix populate_english task to auto-fill English translations (msgstr = msgid)
- Task properly handles plural forms and multi-line strings

Phase 1 complete - infrastructure ready for module-by-module migration.
2026-02-02 09:40:32 -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
1414f087fc
bug fix 2026-02-01 17:25:16 -06:00
1790db8d5a
update mikrotik port with/without ssl 2026-02-01 17:09:52 -06:00
23af86ba73
add mikrotik device handling 2026-02-01 16:42:39 -06:00
a66aec675e Merge branch 'gmcintire/user-first-last-name' into 'main'
Replace single name field with first_name and last_name

See merge request towerops/towerops!3
2026-02-01 15:00:24 -06:00
e46ecbfca2 Replace single name field with first_name and last_name 2026-02-01 15:00:23 -06:00
91517aa934 Merge branch 'gmcintire/security-audit' into 'main'
Add rate limiting to auth and API endpoints using Hammer

See merge request towerops/towerops!2
2026-02-01 14:59:54 -06:00
02833d0b44 Add rate limiting to auth and API endpoints using Hammer 2026-02-01 14:59:54 -06:00
f0d6ae42d0
feat: update UserAuth to redirect to sudo verify page 2026-02-01 14:58:07 -06:00
9ff9c4ddc8
fix: address UserSudoController critical issues
Critical fixes:
- Renamed controller actions to follow Phoenix conventions (verify→new, create→verify)
- Added sudo mode check in GET action using last_sudo_at timestamp
- Added TOTP enrollment check in GET action with redirect
- Changed default return path from /orgs to /users/settings
- Updated route paths from /users/sudo/verify to /users/sudo-verify (dash separator)
- Added success flash message "Identity verified."

Implementation details:
- new/2 (GET): Checks recently_verified_sudo?/1 helper that examines last_sudo_at
  instead of authenticated_at to distinguish sudo verification from regular login
- verify/2 (POST): Adds info flash and defaults to /users/settings
- recently_verified_sudo?/1: Private helper checking last_sudo_at within 10 minutes
- Updated all routes, templates, and tests to use new paths and action names

Tests:
- Added tests for already-in-sudo-mode redirect behavior
- Added test for TOTP enrollment requirement
- Updated all test assertions for new route paths and success messages
- All 15 controller tests passing
- Full test suite passing (4076 tests, 0 failures)
2026-02-01 14:45:05 -06:00
264154a3d8
feat: implement sudo mode MFA-only verification controller
- Add UserSudoController with GET and POST /users/sudo/verify routes
- Create verify.html.heex template for TOTP verification form
- Only accept TOTP codes (6 numeric digits), reject recovery codes
- Update grant_sudo_mode to set authenticated_at virtual field
- Exclude /users/sudo paths from return_to overwriting
- Add comprehensive controller tests (12 test cases)
- Verify redirect behavior, error handling, and session management

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 14:34:17 -06:00
b241d9df91
show cloud poller device count 2026-02-01 13:41:01 -06:00
2a5622d95a
impersonate fix and change log level for device polling 2026-02-01 13:33:36 -06:00
04c33b0719
paginate audit logs 2026-02-01 13:27:56 -06:00
7232dab601
live device polling 2026-02-01 13:19:32 -06:00
6bdd90fd97
add admin links 2026-02-01 12:37:50 -06:00
612bed2dd5
mikrotik version upgrade tracking 2026-02-01 12:35:41 -06:00
7ca07010ab
Use detected timezone from session in user registration 2026-02-01 12:18:33 -06:00
77e62df9da
Move CaptureTimezone plug to browser pipeline
The plug was incorrectly placed in the endpoint, causing it to run on
all requests including APIs. Moved to :browser pipeline where it belongs,
and removed redundant fetch_session call since the pipeline handles it.
2026-02-01 11:15:51 -06:00
d05c493943
Add CaptureTimezone plug to browser pipeline 2026-02-01 11:10:34 -06:00