panel: disable demote and delete for the only global admin
test / test (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-11 22:42:01 +03:00
parent 5985977f21
commit de6f7a88da
4 changed files with 25 additions and 2 deletions
+9
View File
@@ -132,9 +132,18 @@ func (h *Handlers) renderUserForm(w http.ResponseWriter, r *http.Request, status
data["FormDomains"] = view.FormDomains
data["FormPassword"] = view.FormPassword
data["IsEdit"] = userID != 0
data["LastGlobalLocked"] = lastGlobalLocked(h, userID, view.FormRole)
h.view.Render(w, status, "user_form", data)
}
func lastGlobalLocked(h *Handlers, userID int64, formRole string) bool {
if userID == 0 || formRole != string(store.RoleGlobal) {
return false
}
n, err := h.store.CountGlobalUsers()
return err == nil && n <= 1
}
func (h *Handlers) submitUserCreate(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
h.renderUserForm(w, r, http.StatusBadRequest, 0, userFormView{FormErr: "Invalid form submission."})
+8
View File
@@ -193,6 +193,14 @@ button, a.btn, a.danger {
color: var(--on-accent); background: var(--accent-fill); border: 0; border-radius: 5px; cursor: pointer;
}
button:hover, a.btn:hover { background: var(--accent-fill-hover); }
button:disabled, button.danger:disabled {
opacity: 0.45; cursor: not-allowed;
}
button:disabled:hover { background: var(--accent-fill); }
button.danger:disabled:hover { background: var(--danger-fill); }
select:disabled {
opacity: 0.65; cursor: not-allowed; background: var(--code-bg);
}
.error { color: var(--danger-fg); margin: 0.6rem 0 0; font-weight: 600; }
.muted { color: var(--muted); }
/* Links take the accent. The panel never set a colour here and ran on the
+5 -2
View File
@@ -14,10 +14,12 @@
<input id="password" name="password" type="password" autocomplete="new-password" {{if not .IsEdit}}required{{end}}>
<label for="role">Role</label>
<select id="role" name="role" data-global-role="{{.GlobalRole}}">
{{if .LastGlobalLocked}}<input type="hidden" name="role" value="{{.FormRole}}">{{end}}
<select id="role" name="role" data-global-role="{{.GlobalRole}}"{{if .LastGlobalLocked}} disabled{{end}}>
<option value="domain_admin" {{if eq .FormRole "domain_admin"}}selected{{end}}>Domain administrator</option>
<option value="global" {{if eq .FormRole "global"}}selected{{end}}>Global administrator</option>
</select>
{{if .LastGlobalLocked}}<p class="muted">The only global administrator cannot be demoted.</p>{{end}}
<fieldset id="domain-pick" data-domain-pick>
<legend>Assigned domains</legend>
@@ -32,7 +34,8 @@
<button type="submit">{{if .IsEdit}}Save{{else}}Create{{end}}</button>
{{if .IsEdit}}
<button type="submit" name="action" value="delete" class="danger">Delete user</button>
<button type="submit" name="action" value="delete" class="danger"{{if .LastGlobalLocked}} disabled{{end}}>Delete user</button>
{{if .LastGlobalLocked}}<p class="muted">The only global administrator cannot be deleted.</p>{{end}}
{{end}}
</form>
</div>