From 87cdf79b2801de768712594997f102b593acd835 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Thu, 5 Nov 2026 20:17:42 -0500 Subject: [PATCH] Make managed mode off by default and turned on per side in config.php - $config_enable_managed_instances = true; adds Admin > Managed Instances, the api/managed endpoints and the Managed Instances cron job, and $config_enable_managed_by = true; (or $config_manager_required = true;) adds Admin > Managed By, the Manager Check-in cron job and support sign-in; with a side off its menu entry, pages, modal, post handler and cron job are gone, the manager endpoints answer 503 so instances keep their connection and retry, a connected instance stops checking in and its locks, read-only and suspended lift while its connection is kept, any open support session is ended, and the support user still takes no seat; new managerEnabled(), managedEnabled() and managedConnected(), the cron registry and health check leave out the jobs of a side that is off, changelog updated --- CHANGELOG.md | 5 +- admin/includes/side_nav.php | 46 ++++++++------- admin/managed_by.php | 16 +++++ admin/managed_instance.php | 3 +- admin/managed_instances.php | 8 +++ .../managed_instance_form.php | 10 +++- admin/post/managed_by.php | 5 ++ admin/post/managed_instances.php | 5 ++ api/managed/bootstrap.php | 7 +++ cron/managed_instances.php | 5 ++ cron/manager_checkin.php | 5 ++ functions/managed.php | 59 ++++++++++++++++++- functions/system_health.php | 4 ++ includes/cron_jobs.php | 16 ++++- 14 files changed, 167 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc046f5cd..7bdf419e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ This file documents all notable changes made to ITFlow. ### Upgrading -Update from Maintenance > Update — Update ITFlow hands the job to cron, which updates the files and applies this release's five database changes (3.5.4 to 3.5.9) in the same pass. Nothing else is required. Everyone is signed out once after the update, agents and client portal contacts alike - see Security. The new Payment Provider Fees, Manager Check-in and Managed Instances jobs in Maintenance > Cron are set up by themselves. +Update from Maintenance > Update — Update ITFlow hands the job to cron, which updates the files and applies this release's five database changes (3.5.4 to 3.5.9) in the same pass. Nothing else is required. Everyone is signed out once after the update, agents and client portal contacts alike - see Security. The new Payment Provider Fees job in Maintenance > Cron is set up by itself, and so are Manager Check-in and Managed Instances on an install whose config.php turns managed mode on (see New Features & Updates). ### Security @@ -27,7 +27,7 @@ Update from Maintenance > Update — Update ITFlow hands the job to cron, which - API keys: agents are notified in ITFlow a week before an API key expires, so an integration doesn't stop without warning. - Payment Providers: Stripe's fee on a card payment is now recorded as an expense about 15 minutes after the payment, instead of the next morning. A new Payment Provider Fees job in Maintenance > Cron does it, and you can run it more often there. Record fees now still works any time. - Payment Providers: clients can pay the card fee. Turn on Clients pay the card fee on Stripe in Admin > Payment Providers and enter what Stripe charges you, as 2.9% plus 0.30. Each card payment is then made bigger by Stripe's fee on it, so you get the full invoice. The fee goes on the invoice as a Card Fee line, with no tax, when the payment goes through. Clients see it before they pay: on Pay invoices and the checkout page in the client portal, on the page an invoice's link opens, and next to autopay. It applies to payments in the client portal, through an invoice's link, by autopay, and when you charge a saved card, where the Charge Saved Card window shows it too. One payment for several invoices has one fee, shared over them. A client can be let off it with No card fee on their Billing tab. Refunding a card payment from ITFlow takes its Card Fee line back off the invoice. It is off until you turn it on. Card surcharges have rules where you and your clients are - in the US, for one, the card networks cap them and don't allow them on debit cards - so check them before turning it on. -- Managed mode: one ITFlow can now manage others - a client's, a reseller's or your own hosted ones. Add one in the new Admin > Managed Instances, and paste the code it gives you into the other ITFlow's Admin > Managed By. From then on that ITFlow checks in every minute, and you see how each one is doing: its version and whether an update is waiting (a security one stands out), whether cron is running, its last backup, the errors it logged today, and its agents against its seat limit - and you're notified when one stops checking in. From the manager you can update an instance, or let it update itself a set number of days after a release comes out and straight away for a security release; set how many agents it can have and its white-label key; make it read-only, where its people can still sign in, look and export, or suspend it, with a message of your own; and sign in to it as support in a new tab, for two hours, with no password or seat of its own and every sign-in in its audit log. What the manager sets is locked on the instance, which says so - everything else stays the instance's own, and its admins can disconnect it unless its config.php keeps it managed (`$config_manager_required = true`, for a provider hosting it). Instances only ever call the manager, so they can sit behind a firewall, and they send how they're doing - never client data. +- Managed mode: one ITFlow can now manage others - a client's, a reseller's or your own hosted ones. It's off until config.php turns it on, each side on its own: `$config_enable_managed_instances = true;` on the ITFlow that does the managing adds Admin > Managed Instances, and `$config_enable_managed_by = true;` on each one it manages adds Admin > Managed By. An install with neither line sees nothing of it - no menu, pages, cron jobs or sign-in. Then add one in Admin > Managed Instances, and paste the code it gives you into the other ITFlow's Admin > Managed By. From then on that ITFlow checks in every minute, and you see how each one is doing: its version and whether an update is waiting (a security one stands out), whether cron is running, its last backup, the errors it logged today, and its agents against its seat limit - and you're notified when one stops checking in. From the manager you can update an instance, or let it update itself a set number of days after a release comes out and straight away for a security release; set how many agents it can have and its white-label key; make it read-only, where its people can still sign in, look and export, or suspend it, with a message of your own; and sign in to it as support in a new tab, for two hours, with no password or seat of its own and every sign-in in its audit log. What the manager sets is locked on the instance, which says so - everything else stays the instance's own, and its admins can disconnect it unless its config.php keeps it managed (`$config_manager_required = true`, for a provider hosting it, which turns Managed By on as well). Instances only ever call the manager, so they can sit behind a firewall, and they send how they're doing - never client data. - Export Data: the documentation PDF can go out as a secure link, like a login, document or file. Share a link next to Download PDF - or Share the IT documentation on a client's Shared Links - makes the PDF from the parts you tick and gives you a link that works for as long and as many times as you set, with a passphrase or an emailed code first if you like, and tells you when it's downloaded. The PDF is made when the link is, so later changes aren't in it, and it's kept encrypted until the link stops working - then it's deleted. With passwords and 2FA secrets in it, the PDF is locked with a password of its own too, as a download is, and the page the link opens says so. - Maintenance > Update is easier to follow. It opens with where your install stands - up to date, an update available, an update running, or the database still to finish - and the one button for it, now Update ITFlow instead of Queue Update. Below it is what's new: the release notes for the versions between yours and the latest, read from the changelog each time ITFlow checks for updates, with the developer notes folded away and the commits behind them a click further. Before you update it lists what to do first, and says whether any of ITFlow's own files have been changed on your install, which updating would put back. Once you click Update ITFlow, the page shows the update running, refreshes itself until it's done, and then says when ITFlow was updated, or what stopped it. Alongside are this install's version, branch, database, when it last checked and updated, whether cron is running, and the command to run the same update from a shell, with the folder and user filled in for your server. @@ -71,6 +71,7 @@ Update from Maintenance > Update — Update ITFlow hands the job to cron, which - `managedRequestGuard($surface)` enforces the mode: `agent` in `includes/check_login.php`, `portal` in `client/includes/check_login.php`, `api` in `api/v1/validate_api_key.php`, `login` in `login.php` and `client/login_microsoft.php` (suspended only), and `public` at the end of `includes/load_global_settings.php`, which nearly every guest and pre-sign-in page loads (guarded by `function_exists()`, as some portal pages load it before `functions.php`). Read-only refuses any POST and any request to a `post.php` or `guest_post.php` - but not signing out, MFA set-up, a key starting `export` or `download`, or `managed_checkin_now` - with a flash, a 403 JSON for ajax and the API, or `includes/managed_blocked.php` without a session; suspended shows that page (503), or 503 JSON for the API. `managedBannerHtml()` (from `includes/inc_alert_feedback.php` and the portal header) shows read-only and support sessions. - Cron: `cronJobClaim($mysqli, $job, $paused)` doesn't claim a paused job - a due one gets `Stopped: Paused - ...` and stays due, Run Now included. `cron/cron.php` pauses every job but `MANAGED_KEEP_JOBS` while `managedMode()` isn't active, asking again for each job so a check-in at the top of the cycle applies to the rest of it. - Locks: `getAgentUserLimit()` returns `managedSeatLimit()` when the manager sets one, and `getActiveAgentUserCount()` leaves out `managedSupportUserId()`. `managedApplyState()` writes the manager's white-label key to `config_whitelabel_key` and takes it out again when the manager stops setting it, and Settings > Modules doesn't save over it. `admin/update.php` hides Update ITFlow and `queue_update` refuses on a managed instance. `managedRequired()` reads `$config_manager_required`. +- Off by default: `managerEnabled()` reads `$config_enable_managed_instances` and `managedEnabled()` reads `$config_enable_managed_by` (or `$config_manager_required`). Off, the side nav leaves out that side's entry (and the MANAGEMENT header when both are off), its pages and the instance modal say how to turn it on, its `admin/post/` handler returns at the top, `cronJobRegistry()` leaves out its cron job - so Maintenance > Cron doesn't show it, the dispatcher doesn't run it and the health check skips its old row - and each job stops itself if run by hand. With the manager side off, `api/managed/bootstrap.php` answers both endpoints 503 before anything else, which an instance records as its last error and keeps its connection. With Managed By off, `managedIsManaged()` is false while `settings.config_managed` is kept (the new `managedConnected()` says whether it's there): no check-ins, locks, read-only or suspended, `managedEnroll()` and `managedVerifySupportToken()` refuse, and `managedRequestGuard()` ends any session with `$_SESSION['managed_support']`. `managedSupportUserId()` follows `managedConnected()`, so the support user still takes no seat. - `cron/ticket_email_parser.php` no longer writes an App Logs error when cron or parsing is switched off - `cronJobStop()` already records why, and with parsing off it was an error a minute, which `managedReport()` counts. The audit log knows the Managed Instance and Management types and the Connect, Disconnect, Remove, Release, Code, Mode and Support actions. - Shared links have a new type, `Export` (IT documentation). `agent/ajax.php` share_create makes its PDF with `clientExportOptions()`, `clientExportCheck()` and `clientExportPdf()`, as the download does, and hands it to `shareCreate()` as `export` (pdf, name, secret). The link seals the PDF's name, size, SHA-256 and whether it's password-locked into `item_payload`, and `shareExportStore()` writes the PDF to `uploads/shares/export-.bin` with AES-256-GCM under the link's content key - iv, tag, ciphertext - in a folder `backupHardenStorageDir()` keeps from the web, so the disk and the database alone open nothing. `guest/guest_download_file.php` serves File or Export, the PDF through `shareExportRead()`; `guest/share_ajax.php` describes it from `shareExportSize()` and the payload; `js/guest_share.js` downloads it as it does a file. `shareEnd()` and `shareSweep()` delete the file with the payload, and `sharePrune()` deletes any left without a link that can still open it (a client deleted with its links). `shareNeedsProtection($type, $secret)` takes whether this link holds passwords, `shareCanSee()` and `shareCanMake()` need `module_client` for it, and it's logged as a Client Export too. No database change - `item_type` is a varchar. `clientExportSectionPicker()` takes an id prefix and the sections to start ticked; Export Data's Share a link passes the ticked ones as `sections[]`. - `api/v1/tickets/create.php` takes `ticket_template_id`, `ticket_fill_ins` (a JSON object of fill-in names and answers, checked with `ticketFillInAnswers()`) and `ticket_notify`. A missing or archived template, `ticket_fill_ins` that isn't an object and a required fill-in left out are a 400 with the reason in `message`. From a template, `ticket_subject`, `ticket_details`, `ticket_priority`, `ticket_billable` and `ticket_assigned_to` not sent come from it, `ticket_category` is its category, and `ticketTemplateFinish($ticket_id, $template_id, true, true, $answers, ...)` adds its tags, due time, tasks and fill-in answers and fills its placeholders. `ticket_notify` = 1 calls `ticketMailCreated()` and `ticketMailColleaguesNew()` when `config_ticket_client_general_notifications` is on, `ticketMailNewTicketAddress($ticket_id, 'api')`, and for an assigned ticket an app notification and `ticketMailAssigned()`. The answer adds `ticket_reference`, `ticket_url` (`ticketEmailUrl()`) and, from a template, `ticket_tasks`. `ticketAgentEmailContent()`'s `new` takes how `api` ("raised a ticket through the API"). A request without the three fields is raised as before, with the two new fields in its answer. diff --git a/admin/includes/side_nav.php b/admin/includes/side_nav.php index 5a626a236..5c7800347 100644 --- a/admin/includes/side_nav.php +++ b/admin/includes/side_nav.php @@ -180,27 +180,33 @@ - - + + + + - - + + + + + + diff --git a/admin/managed_by.php b/admin/managed_by.php index d03fedd60..dde7791cd 100644 --- a/admin/managed_by.php +++ b/admin/managed_by.php @@ -6,10 +6,26 @@ * how its check-ins are going, what the manager sets here - and so what's locked - and exactly * what this ITFlow tells it. Disconnecting is here too, unless config.php keeps it managed * ($config_manager_required). The handlers are admin/post/managed_by.php. + * + * Off unless config.php turns it on ($config_enable_managed_by, managedEnabled()) - then the + * page says how, and whether this ITFlow is still connected to a manager from before. */ require_once "includes/inc_all_admin.php"; +if (!managedEnabled()) { + $managed_off_connected = settingsColumnExists($mysqli, 'config_managed') && managedConnected(); + $managed_off_from = trim((string) (managedConfig()['manager_name'] ?? '')); ?> +
Being managed by another ITFlow is off. To turn it on, add this line to the end of config.php: $config_enable_managed_by = true; + +
This ITFlow is still connected to , but doesn't check in while this is off, and nothing it set is locked here. Turn it back on to carry on, or to disconnect.
+ +
+
Being managed by another ITFlow needs this install's database update - apply it from Maintenance > Update first.
+
Managing other ITFlow installs is off. To turn it on, add this line to the end of config.php: $config_enable_managed_instances = true;
+
Managing other ITFlow installs needs this install's database update - apply it from Maintenance > Update first.
diff --git a/admin/modals/managed_instance/managed_instance_form.php b/admin/modals/managed_instance/managed_instance_form.php index d194b8b97..26c50428b 100644 --- a/admin/modals/managed_instance/managed_instance_form.php +++ b/admin/modals/managed_instance/managed_instance_form.php @@ -11,12 +11,18 @@ require_once '../../includes/modal_header.php'; enforceAdminPermission(); -$instance = isset($_GET['id']) ? managedInstanceRow(intval($_GET['id'])) : null; +$instance = managerEnabled() && isset($_GET['id']) ? managedInstanceRow(intval($_GET['id'])) : null; $is_new = $instance === null; ob_start(); -if (!$is_new && !empty($instance['managed_instance_archived_at'])) { ?> +if (!managerEnabled()) { ?> + + +