Area: Panel and notifications
The problem
Retyping the justification is a large part of the daily PIM tax. Organisations that require
justification (or a ticket number) for activation get one of two bad outcomes: people type work a
hundred times, which makes the audit trail worthless, or they type something real every time, which
makes activation tedious enough that they look for ways around it.
Elevate already does part of this well:
- A profile carries an optional
reason that prefills the run sheet's justification, and managed
profiles pushed via ManagedProfiles / ManagedProfilesUrl can carry one too
(docs/enterprise/profiles.md).
- Per-role reason and duration are remembered and offered as the default next time
(elevate activate falls back to what was used last for that role, then to the policy).
RolePolicy knows requiresJustification and requiresTicket, so the run sheet can ask.
What is missing is everything between "one remembered string per role" and "type it fresh":
- No history or picker. Only the single last reason per role is offered. A user who alternates
between three recurring reasons retypes two of them every time.
- No templates. There is no way to define "Incident {ticket}", "Change {ticket}", "Access review"
once and pick from them across roles.
- No enforced format. An organisation that requires justifications to reference a ticket
(INC\d+, CHG\d+) has no way to say so. The managed key set is ClientId,
DisableUpdateCheck, AllowedSignInMethods, AllowedTenants, PinnedTenants,
ManagedProfiles, ManagedProfilesUrl and the four Organization* keys — nothing about
justification content. Elevate will happily submit asdf.
What you would like Elevate to do
Reason history and templates. Keep the last several justifications per account (not just per
role) and offer them in the run sheet as a picker, most recent first, still freely editable. Let a
user save one as a named template.
Org-defined templates. A managed key — say JustificationTemplates, a list of strings — that
publishes the reasons an organisation wants people to use, shown at the top of the picker.
Org-enforced format. A managed key — say JustificationPattern (a regular expression) plus
JustificationPatternMessage (what to tell the user when it does not match) — validated in the run
sheet before the request is sent, and in elevate activate --reason before the call. On failure,
show the message, not a regex. Locked with the usual "Managed by your organization" caption.
A minimum-length variant (JustificationMinimumLength) may be worth having alongside, since it
catches work without anyone having to write a regex.
All of it configured inline, through the existing enterprise packaging
These belong in the enterprise kit next to the keys that are already there, and — unlike
ManagedProfiles — none of them needs a URL companion.
ManagedProfilesUrl exists because a profile set is a JSON document that can grow past what is
comfortable to paste into a policy, and because an organisation wants to change it without
re-deploying a profile. Justification templates and a pattern are a short list of short strings and
two scalars. They fit inline in every transport Elevate already reads:
| Transport |
Shape |
macOS managed preferences (no.reothor.elevate) |
An array of strings and two strings, forced values only |
| Windows policy registry |
A REG_MULTI_SZ and two REG_SZ under HKLM\SOFTWARE\Policies\Reothor\Elevate / HKCU |
| ADMX template |
A list box and two text boxes |
| Jamf schema / Intune mobileconfig |
An array and two string properties |
CLI managed.json |
An array and two strings |
So the work is: three (or four) keys in docs/enterprise/keys.md with the rest, the matching entries
in the ADMX, mobileconfig, Jamf schema and managed.json templates under enterprise/, and the
usual precedence and locking behaviour — a managed value wins over the user's and renders disabled
with the "Managed by your organization" caption. No new delivery mechanism, no fetch, nothing to
host.
If an organisation later wants a large or centrally-maintained template library, a
JustificationTemplatesUrl could follow the ManagedProfilesUrl pattern — but it should not be in
the first cut. Adding a daily fetch for a handful of short strings is not worth the moving parts.
Same rules in the CLI. elevate activate --reason and elevate run must apply the pattern, so a
script cannot bypass what the panel enforces.
Alternatives considered
- Rely on PIM's own
requiresJustification. It only enforces that something was typed. It has
no notion of content, which is exactly the gap.
- Rely on the ticket field.
requiresTicket covers the ticket-number case but not the general
"say something meaningful" case, and not every org routes through a ticket system.
- Push everything through managed profiles. A profile's
reason works well for the recurring,
pre-planned activation. It does not help the ad-hoc one, which is when justification quality
matters most.
Anything else
- This is enforcement at the client, not at the service. Someone using the portal directly is
unaffected. Worth saying plainly in the docs so nobody mistakes it for a security control — it is
a data-quality and convenience feature.
- Ticket-system integration (pull the active ticket from ServiceNow / Jira / Azure DevOps and use it
as the justification) is the natural next step and a separate issue.
Area: Panel and notifications
The problem
Retyping the justification is a large part of the daily PIM tax. Organisations that require
justification (or a ticket number) for activation get one of two bad outcomes: people type
workahundred times, which makes the audit trail worthless, or they type something real every time, which
makes activation tedious enough that they look for ways around it.
Elevate already does part of this well:
reasonthat prefills the run sheet's justification, and managedprofiles pushed via
ManagedProfiles/ManagedProfilesUrlcan carry one too(
docs/enterprise/profiles.md).(
elevate activatefalls back to what was used last for that role, then to the policy).RolePolicyknowsrequiresJustificationandrequiresTicket, so the run sheet can ask.What is missing is everything between "one remembered string per role" and "type it fresh":
between three recurring reasons retypes two of them every time.
once and pick from them across roles.
(
INC\d+,CHG\d+) has no way to say so. The managed key set isClientId,DisableUpdateCheck,AllowedSignInMethods,AllowedTenants,PinnedTenants,ManagedProfiles,ManagedProfilesUrland the fourOrganization*keys — nothing aboutjustification content. Elevate will happily submit
asdf.What you would like Elevate to do
Reason history and templates. Keep the last several justifications per account (not just per
role) and offer them in the run sheet as a picker, most recent first, still freely editable. Let a
user save one as a named template.
Org-defined templates. A managed key — say
JustificationTemplates, a list of strings — thatpublishes the reasons an organisation wants people to use, shown at the top of the picker.
Org-enforced format. A managed key — say
JustificationPattern(a regular expression) plusJustificationPatternMessage(what to tell the user when it does not match) — validated in the runsheet before the request is sent, and in
elevate activate --reasonbefore the call. On failure,show the message, not a regex. Locked with the usual "Managed by your organization" caption.
A minimum-length variant (
JustificationMinimumLength) may be worth having alongside, since itcatches
workwithout anyone having to write a regex.All of it configured inline, through the existing enterprise packaging
These belong in the enterprise kit next to the keys that are already there, and — unlike
ManagedProfiles— none of them needs a URL companion.ManagedProfilesUrlexists because a profile set is a JSON document that can grow past what iscomfortable to paste into a policy, and because an organisation wants to change it without
re-deploying a profile. Justification templates and a pattern are a short list of short strings and
two scalars. They fit inline in every transport Elevate already reads:
no.reothor.elevate)REG_MULTI_SZand twoREG_SZunderHKLM\SOFTWARE\Policies\Reothor\Elevate/HKCUmanaged.jsonSo the work is: three (or four) keys in
docs/enterprise/keys.mdwith the rest, the matching entriesin the ADMX, mobileconfig, Jamf schema and
managed.jsontemplates underenterprise/, and theusual precedence and locking behaviour — a managed value wins over the user's and renders disabled
with the "Managed by your organization" caption. No new delivery mechanism, no fetch, nothing to
host.
If an organisation later wants a large or centrally-maintained template library, a
JustificationTemplatesUrlcould follow theManagedProfilesUrlpattern — but it should not be inthe first cut. Adding a daily fetch for a handful of short strings is not worth the moving parts.
Same rules in the CLI.
elevate activate --reasonandelevate runmust apply the pattern, so ascript cannot bypass what the panel enforces.
Alternatives considered
requiresJustification. It only enforces that something was typed. It hasno notion of content, which is exactly the gap.
requiresTicketcovers the ticket-number case but not the general"say something meaningful" case, and not every org routes through a ticket system.
reasonworks well for the recurring,pre-planned activation. It does not help the ad-hoc one, which is when justification quality
matters most.
Anything else
unaffected. Worth saying plainly in the docs so nobody mistakes it for a security control — it is
a data-quality and convenience feature.
as the justification) is the natural next step and a separate issue.