feat(server): refuse local edits to hq-sourced users
The API is the boundary; hiding the control in web/ is a courtesy. A role editable in two places is a role with two answers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,16 @@ import (
|
||||
|
||||
var ErrLastOwner = errors.New("this is the organization's last owner promote another member to owner first")
|
||||
|
||||
// ErrHQManaged is returned when a caller tries to change a user this instance
|
||||
// does not own.
|
||||
//
|
||||
// An hq-sourced row is projected from a Vantage HQ account: HQ owns its role,
|
||||
// its password and its existence. A role editable in two places is a role with
|
||||
// two answers, and the loser is whichever writer ran first. Refusing here
|
||||
// rather than merely hiding the control in web/ is the point — the API is the
|
||||
// boundary, the UI is a courtesy.
|
||||
var ErrHQManaged = errors.New("this member is managed in Vantage HQ; change their role or remove them from the HQ portal")
|
||||
|
||||
func CountUsers() (int64, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
@@ -122,6 +132,9 @@ func UpdateUserRole(instanceID, userID, role string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found")
|
||||
}
|
||||
if target.AuthSource == models.AuthHQ {
|
||||
return ErrHQManaged
|
||||
}
|
||||
|
||||
if target.Role == models.RoleOwner && role != models.RoleOwner {
|
||||
others, err := countOtherOwners(instanceID, userID)
|
||||
@@ -146,6 +159,9 @@ func DeleteUser(instanceID, userID string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found")
|
||||
}
|
||||
if target.AuthSource == models.AuthHQ {
|
||||
return ErrHQManaged
|
||||
}
|
||||
if target.Role == models.RoleOwner {
|
||||
others, err := countOtherOwners(instanceID, userID)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user