fix: add observer and observer plus to lock/unlock permissions (#16886)

> Related issue: #16878

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [x] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [ ] Documented any permissions changes (docs/Using
Fleet/manage-access.md)
- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Jahziel Villasana-Espinoza 2024-02-15 12:40:36 -05:00 committed by GitHub
parent d5a9e8f557
commit bd66898d38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 53 additions and 8 deletions

View file

@ -0,0 +1,2 @@
- Fixes permission issues with lock/unlock functionality. Observer and Observer Plus are now able to
lock/unlock hosts.

View file

@ -51,7 +51,7 @@ func (svc *Service) LockHost(ctx context.Context, hostID uint) error {
// Authorize again with team loaded now that we have the host's team_id.
// Authorize as "execute mdm_command", which is the correct access
// requirement and is what happens for macOS platforms.
if err := svc.authz.Authorize(ctx, fleet.MDMCommandAuthz{TeamID: host.TeamID}, fleet.ActionWrite); err != nil {
if err := svc.authz.Authorize(ctx, fleet.MDMHostActionAuthz{TeamID: host.TeamID}, fleet.ActionWrite); err != nil {
return err
}
@ -133,7 +133,7 @@ func (svc *Service) UnlockHost(ctx context.Context, hostID uint) (string, error)
// Authorize again with team loaded now that we have the host's team_id.
// Authorize as "execute mdm_command", which is the correct access
// requirement.
if err := svc.authz.Authorize(ctx, fleet.MDMCommandAuthz{TeamID: host.TeamID}, fleet.ActionWrite); err != nil {
if err := svc.authz.Authorize(ctx, fleet.MDMHostActionAuthz{TeamID: host.TeamID}, fleet.ActionWrite); err != nil {
return "", err
}

View file

@ -800,6 +800,27 @@ allow {
action == read
}
##
# MDM Actions (lock, unlock, wipe, etc.)
##
# Team admins, maintainers, observers, and observer_plus can write (execute) MDM actions on hosts of their teams.
allow {
not is_null(object.team_id)
object.type == "mdm_action"
team_role(subject, object.team_id) == [admin, maintainer, observer, observer_plus][_]
action == write
}
# Global admins, maintainers, observers, and observer_plus can write (execute) MDM actions on hosts.
allow {
object.type == "mdm_action"
subject.global_role == [admin, maintainer, observer, observer_plus][_]
action == write
}
##
# Cron schedules
##

View file

@ -513,3 +513,13 @@ func MDMProfileSpecsMatch(a, b []MDMProfileSpec) bool {
return len(pathLabelCounts) == 0
}
// MDMHostActionAuthz is used to check authorization for executing MDM actions on a host, e.g. lock,
// unlock, wipe, etc.
type MDMHostActionAuthz struct {
TeamID *uint `json:"team_id"` // required for authorization by team
}
func (m MDMHostActionAuthz) AuthzType() string {
return "mdm_action"
}

View file

@ -1518,26 +1518,26 @@ func TestLockUnlockHostAuth(t *testing.T) {
{
name: "global observer",
user: &fleet.User{GlobalRole: ptr.String(fleet.RoleObserver)},
shouldFailGlobalWrite: true,
shouldFailTeamWrite: true,
shouldFailGlobalWrite: false,
shouldFailTeamWrite: false,
},
{
name: "team observer",
user: &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleObserver}}},
shouldFailGlobalWrite: true,
shouldFailTeamWrite: true,
shouldFailTeamWrite: false,
},
{
name: "global observer plus",
user: &fleet.User{GlobalRole: ptr.String(fleet.RoleObserverPlus)},
shouldFailGlobalWrite: true,
shouldFailTeamWrite: true,
shouldFailGlobalWrite: false,
shouldFailTeamWrite: false,
},
{
name: "team observer plus",
user: &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleObserverPlus}}},
shouldFailGlobalWrite: true,
shouldFailTeamWrite: true,
shouldFailTeamWrite: false,
},
{
name: "global admin",
@ -1575,6 +1575,18 @@ func TestLockUnlockHostAuth(t *testing.T) {
shouldFailGlobalWrite: true,
shouldFailTeamWrite: true,
},
{
name: "team observer wrong team",
user: &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 42}, Role: fleet.RoleObserver}}},
shouldFailGlobalWrite: true,
shouldFailTeamWrite: true,
},
{
name: "team observer plus wrong team",
user: &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 42}, Role: fleet.RoleObserverPlus}}},
shouldFailGlobalWrite: true,
shouldFailTeamWrite: true,
},
{
name: "global gitops",
user: &fleet.User{GlobalRole: ptr.String(fleet.RoleGitOps)},