From 27b56db6b0876f3cdae7c04893fd31ceadf4c686 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Sun, 25 Oct 2026 20:58:27 -0700 Subject: [PATCH] case 4735 - role rights match the documentation Audit of every documented role right against BizRoles, decided with John: Tech restricted reads tax codes, projects, unit models and meter readings; Business administration reads server metrics; Accounting no longer enters meter readings (both meter reading routes checked Unit rights, now meter reading rights); no Subcontractor roles on memos; integrations: data operations open to all full staff roles, switching Active and deleting are Business administration only. Integrated applications page and changelog. Co-Authored-By: Claude Opus 5.5 --- devdocs/modernization/NET10-PORT.md | 2 +- .../docs/api-integrated-applications.md | 2 ++ docs/8.0/ayanova/docs/changelog.md | 1 + server/AyaNova/Controllers/UnitController.cs | 6 ++++-- server/AyaNova/biz/BizRoles.cs | 21 ++++++++++--------- server/AyaNova/biz/IntegrationBiz.cs | 9 +++++++- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/devdocs/modernization/NET10-PORT.md b/devdocs/modernization/NET10-PORT.md index 9ae4c6be..84a5ece5 100644 --- a/devdocs/modernization/NET10-PORT.md +++ b/devdocs/modernization/NET10-PORT.md @@ -202,7 +202,7 @@ John's "next release" cases for AyaNova (exported 2026-10-24). Each is its own s | Case | Name | Priority | |---|---|---| -| 4735 | Roles audit: every documented role right against `BizRoles.cs` (found in 4719, see Deferred) | 00 | +| 4735 | Roles audit: every documented role right against `BizRoles.cs` (found in 4719, see Deferred) | 00. Audit: 60 manual role lists and 19 prose sections against 89 types (script); besides report templates (4719) 15 differences, one a false positive, decided with John 2026-10-25. **Code** (commit 1): Tech restricted reads tax codes, projects, unit models, meter readings; BizAdmin and restricted read server metrics; Accounting no longer changes meter readings (both meter reading routes in `UnitController` checked Unit rights, now meter reading rights); no Subcontractor roles on memos; integrations: data operations stay open to all full staff roles (third-party apps may sign in as any role, QBI as Accounting), switching Active and deleting are BizAdmin only. Test `RoleRights` (6; before: the 5 changed rights behaved the old way; Tech still enters meter readings). **Manual** (commit 2): travel rates role names (the page named non-existent "Travel" roles), customer service requests created by customer contacts. Work orders: Inventory was a false positive of the audit script (it read the page's part cost visibility list), the role lists are right. Subcontractors still cannot enter meter readings (never could; John: keep). Lookups readable by every role are worded in prose, no change | | 4692 | SuperUser can be deleted | 00. **Done** (`UserBiz.ValidateCanDelete` refuses user 1 with 2004 before any other check; the integration test `UserWithActivityShouldNotBeDeleteable`, which deleted SuperUser on a database where it had no related records and so was excluded from every run, is now `SuperUserShouldNotBeDeleteable` and runs with the rest: full suite = baseline, SuperUser still logs in afterwards; changelog) | | 4693 | FormFieldReference start-up race | 00. **Done** (both lazily built lists, `FormFieldReferenceList` and `FormFieldKeys`, are built in a local and published when complete, fields volatile; three restarts each followed at once by the full suite: baseline every time, no "is not valid" in the log) | | 4654 | General notification delivers at most once per 12 hours (circuit breaker) | 00. **Done** (direct messages are general notifications with no object, so the 12-hour repeat check in `CoreJobNotify` (case 3917, keyed on subscription and object) dropped every message to a user after the first; for general notifications the check now applies only to the job's own delivery error messages (`DELIVERY_ERROR_NAME`, "Error"); integration test `DirectMessage_SendAndDelete` un-skipped and now sends two messages: before, only the first arrived; after, both; full suite = baseline; changelog) | diff --git a/docs/8.0/ayanova/docs/api-integrated-applications.md b/docs/8.0/ayanova/docs/api-integrated-applications.md index c6ec7ebc..6b79d11a 100644 --- a/docs/8.0/ayanova/docs/api-integrated-applications.md +++ b/docs/8.0/ayanova/docs/api-integrated-applications.md @@ -17,6 +17,8 @@ One side of this feature is the [Integrated applications User interface](adm-int Using standard AyaNova API routes, developers can leverage this feature to provide configuration and mapping data persistance, logging for troubleshooting and a master on/off "switch" to control the active status of the integrating application. +An integrating application can sign in as a User with any of these full (not restricted) [roles](ay-biz-admin-roles.md) to create its integration record, read and update its data and write to its log: Business administration, Service manager, Inventory, Accounting, Service technician, Sales or System operations. Changing the Active status of an integration and deleting an integration are only available to the Business administration role, as in the [Integrated applications User interface](adm-integrations.md); the application should read the Active status and not change it. + ### API Routes As with all API routes in AyaNova, these API routes can be viewed in the [API explorer console](api-console.md). diff --git a/docs/8.0/ayanova/docs/changelog.md b/docs/8.0/ayanova/docs/changelog.md index 81376c2a..0614f618 100644 --- a/docs/8.0/ayanova/docs/changelog.md +++ b/docs/8.0/ayanova/docs/changelog.md @@ -32,6 +32,7 @@ IN PROGRESS - Server: while a report renders, its template no longer holds the User's actual access token or download key; the AyaNova server adds the User's access only to the report's requests to the `ayApiUrl` address. Templates that call the AyaNova API through any other address need to be changed. See [API usage in reports](ay-report-edit.md#api-usage). - Server: users with the "Business administration - restricted" role can view [report templates](adm-report-templates.md) but no longer create, edit or import them, as described for that [role](ay-biz-admin-roles.md). Give the full "Business administration" role to users who edit report templates. - Server: the SuperUser account can no longer be deleted. +- Server: role rights corrected to match the documentation: "Service technician - restricted" users can view tax codes, projects, unit models and meter readings; "Business administration" users can view server metrics; Accounting users can no longer enter unit meter readings; only "Business administration" users can switch an [integrated application](api-integrated-applications.md) on or off or delete it. - Server: fixed direct notification messages to a user: only the first one within 12 hours was delivered, now every message is. - Server: the server information page at `/api/v8/` no longer shows license details. - Server: business logo uploads must be PNG, JPEG, GIF, WebP or SVG images, and need full rights to global settings. See [business logos](adm-global-logo.md). diff --git a/server/AyaNova/Controllers/UnitController.cs b/server/AyaNova/Controllers/UnitController.cs index 8d79f081..3231b72b 100644 --- a/server/AyaNova/Controllers/UnitController.cs +++ b/server/AyaNova/Controllers/UnitController.cs @@ -397,7 +397,8 @@ namespace AyaNova.Api.Controllers if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); UnitMeterReadingBiz biz = UnitMeterReadingBiz.GetBiz(ct, HttpContext); - if (!Authorized.HasCreateRole(HttpContext.Items, AyaType.Unit)) + //case 4735: meter reading rights (were the Unit rights, which let Accounting enter meter readings) + if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); @@ -419,7 +420,8 @@ namespace AyaNova.Api.Controllers if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); UnitMeterReadingBiz biz = UnitMeterReadingBiz.GetBiz(ct, HttpContext); - if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Unit)) + //case 4735: meter reading rights (were the Unit rights, which let Accounting enter meter readings) + if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); diff --git a/server/AyaNova/biz/BizRoles.cs b/server/AyaNova/biz/BizRoles.cs index 3197a54c..9f422149 100644 --- a/server/AyaNova/biz/BizRoles.cs +++ b/server/AyaNova/biz/BizRoles.cs @@ -297,6 +297,7 @@ namespace AyaNova.Biz | AuthorizationRoles.Sales | AuthorizationRoles.SalesRestricted | AuthorizationRoles.Tech + | AuthorizationRoles.TechRestricted//case 4735: as documented, like Tech , Select = AuthorizationRoles.All }); @@ -344,6 +345,7 @@ namespace AyaNova.Biz ReadFullRecord = AuthorizationRoles.Service | AuthorizationRoles.Sales | AuthorizationRoles.Tech + | AuthorizationRoles.TechRestricted//case 4735: as documented, like Tech | AuthorizationRoles.BizAdminRestricted | AuthorizationRoles.ServiceRestricted | AuthorizationRoles.SalesRestricted @@ -398,7 +400,8 @@ namespace AyaNova.Biz | AuthorizationRoles.ServiceRestricted | AuthorizationRoles.Sales | AuthorizationRoles.SalesRestricted - | AuthorizationRoles.Tech, + | AuthorizationRoles.Tech + | AuthorizationRoles.TechRestricted,//case 4735: as documented, like Tech Select = AuthorizationRoles.All }); @@ -407,11 +410,13 @@ namespace AyaNova.Biz // roles.Add(AyaType.UnitMeterReading, new BizRoleSet() { - Change = AuthorizationRoles.BizAdmin | AuthorizationRoles.Service | AuthorizationRoles.Tech | AuthorizationRoles.Accounting, + //case 4735: Accounting no longer changes meter readings; Tech restricted reads them, as documented + Change = AuthorizationRoles.BizAdmin | AuthorizationRoles.Service | AuthorizationRoles.Tech, ReadFullRecord = AuthorizationRoles.BizAdminRestricted | AuthorizationRoles.ServiceRestricted | AuthorizationRoles.Sales - | AuthorizationRoles.SalesRestricted, + | AuthorizationRoles.SalesRestricted + | AuthorizationRoles.TechRestricted, Select = AuthorizationRoles.All }); @@ -780,7 +785,8 @@ namespace AyaNova.Biz roles.Add(AyaType.ServerMetrics, new BizRoleSet() { Change = AuthorizationRoles.OpsAdmin,//this is to turn on extra metrics (profiler) - ReadFullRecord = AuthorizationRoles.OpsAdmin | AuthorizationRoles.OpsAdminRestricted + //case 4735: Business administration views metrics, as documented and as for the other operations forms + ReadFullRecord = AuthorizationRoles.OpsAdmin | AuthorizationRoles.OpsAdminRestricted | AuthorizationRoles.BizAdmin | AuthorizationRoles.BizAdminRestricted }); @@ -900,6 +906,7 @@ namespace AyaNova.Biz // (everyone but outside users Customer and HO can send and receive memos) roles.Add(AyaType.Memo, new BizRoleSet() { + //case 4735: no Subcontractor roles; subcontractors have no memo feature (as documented) Change = AuthorizationRoles.BizAdminRestricted | AuthorizationRoles.BizAdmin | AuthorizationRoles.ServiceRestricted @@ -909,8 +916,6 @@ namespace AyaNova.Biz | AuthorizationRoles.Accounting | AuthorizationRoles.TechRestricted | AuthorizationRoles.Tech - | AuthorizationRoles.SubContractorRestricted - | AuthorizationRoles.SubContractor | AuthorizationRoles.Sales | AuthorizationRoles.SalesRestricted | AuthorizationRoles.OpsAdminRestricted @@ -924,8 +929,6 @@ namespace AyaNova.Biz | AuthorizationRoles.Accounting | AuthorizationRoles.TechRestricted | AuthorizationRoles.Tech - | AuthorizationRoles.SubContractorRestricted - | AuthorizationRoles.SubContractor | AuthorizationRoles.Sales | AuthorizationRoles.SalesRestricted | AuthorizationRoles.OpsAdminRestricted @@ -939,8 +942,6 @@ namespace AyaNova.Biz | AuthorizationRoles.Accounting | AuthorizationRoles.TechRestricted | AuthorizationRoles.Tech - | AuthorizationRoles.SubContractorRestricted - | AuthorizationRoles.SubContractor | AuthorizationRoles.Sales | AuthorizationRoles.SalesRestricted | AuthorizationRoles.OpsAdminRestricted diff --git a/server/AyaNova/biz/IntegrationBiz.cs b/server/AyaNova/biz/IntegrationBiz.cs index a8aef7c1..669ed19f 100644 --- a/server/AyaNova/biz/IntegrationBiz.cs +++ b/server/AyaNova/biz/IntegrationBiz.cs @@ -234,11 +234,18 @@ namespace AyaNova.Biz } } + //case 4735: an integrated application, whatever role it signs in as, works with its own data; switching an + //integration on or off is for Business administration (Integrated applications form) + if (!isNew && proposedObj.Active != currentObj.Active && !CurrentUserRoles.HasFlag(AuthorizationRoles.BizAdmin)) + AddError(ApiErrorCode.NOT_AUTHORIZED, "Active"); + } private void ValidateCanDelete(Integration inObj) { - //whatever needs to be check to delete this object + //case 4735: deleting an integration is for Business administration (Integrated applications form) + if (!CurrentUserRoles.HasFlag(AuthorizationRoles.BizAdmin)) + AddError(ApiErrorCode.NOT_AUTHORIZED); }