[release-10.11] MM-68547: Tighten authorization on group syncable link and patch endpoints (#36434)

Automatic Merge
Этот коммит содержится в:
Maria A Nunez
2026-05-06 02:53:51 -04:00
коммит произвёл GitHub
родитель 977c791e5b
Коммит 202d125afa
8 изменённых файлов: 1006 добавлений и 23 удалений

Просмотреть файл

@@ -377,10 +377,35 @@ func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
groupSyncable := &model.GroupSyncable{
GroupId: c.Params.GroupId,
SyncableId: syncableID,
Type: syncableType,
appErr = verifySchemeAdminAssignmentPermission(c, syncableType, syncableID, patch)
if appErr != nil {
appErr.Where = "Api4.linkGroupSyncable"
c.Err = appErr
return
}
// Upsert onto the existing row only when it is currently active so
// unspecified fields are preserved. A fresh link, or a re-link of a
// soft-deleted row, starts from a zero-value struct so that fields
// the caller did not (or was not authorized to) set are not carried
// over from the previous incarnation. The downstream upsert clears
// DeleteAt when re-activating.
existing, appErr := c.App.GetGroupSyncable(c.Params.GroupId, syncableID, syncableType)
if appErr != nil && appErr.StatusCode != http.StatusNotFound {
appErr.Where = "Api4.linkGroupSyncable"
c.Err = appErr
return
}
var groupSyncable *model.GroupSyncable
if existing != nil && existing.DeleteAt == 0 {
groupSyncable = existing
} else {
groupSyncable = &model.GroupSyncable{
GroupId: c.Params.GroupId,
SyncableId: syncableID,
Type: syncableType,
}
}
groupSyncable.Patch(patch)
groupSyncable, appErr = c.App.UpsertGroupSyncable(groupSyncable)
@@ -392,8 +417,9 @@ func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddEventResultState(groupSyncable)
auditRec.AddEventObjectType("group_syncable")
syncRoles := patch.SchemeAdmin != nil
c.App.Srv().Go(func() {
c.App.SyncRolesAndMembership(c.AppContext, syncableID, syncableType, c.Params.GroupId)
c.App.SyncRolesAndMembership(c.AppContext, syncableID, syncableType, c.Params.GroupId, syncRoles)
})
w.WriteHeader(http.StatusCreated)
@@ -560,6 +586,13 @@ func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
appErr = verifySchemeAdminAssignmentPermission(c, syncableType, syncableID, patch)
if appErr != nil {
appErr.Where = "Api4.patchGroupSyncable"
c.Err = appErr
return
}
groupSyncable, appErr := c.App.GetGroupSyncable(c.Params.GroupId, syncableID, syncableType)
if appErr != nil {
c.Err = appErr
@@ -577,8 +610,9 @@ func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddEventResultState(groupSyncable)
auditRec.AddEventObjectType("group_syncable")
syncRoles := patch.SchemeAdmin != nil
c.App.Srv().Go(func() {
c.App.SyncRolesAndMembership(c.AppContext, syncableID, syncableType, c.Params.GroupId)
c.App.SyncRolesAndMembership(c.AppContext, syncableID, syncableType, c.Params.GroupId, syncRoles)
})
b, err := json.Marshal(groupSyncable)
@@ -710,6 +744,34 @@ func verifyLinkUnlinkPermission(c *Context, syncableType model.GroupSyncableType
return nil
}
// verifySchemeAdminAssignmentPermission requires the caller to hold the
// role-management permission for the target syncable
// (manage_team_roles / manage_channel_roles), or the sysconsole groups
// write permission, before an explicit SchemeAdmin value in the patch is
// accepted. A nil patch.SchemeAdmin is a no-op.
func verifySchemeAdminAssignmentPermission(c *Context, syncableType model.GroupSyncableType, syncableID string, patch *model.GroupSyncablePatch) *model.AppError {
if patch == nil || patch.SchemeAdmin == nil {
return nil
}
if c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteUserManagementGroups) {
return nil
}
switch syncableType {
case model.GroupSyncableTypeTeam:
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), syncableID, model.PermissionManageTeamRoles) {
return model.MakePermissionError(c.AppContext.Session(), []*model.Permission{model.PermissionManageTeamRoles})
}
case model.GroupSyncableTypeChannel:
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), syncableID, model.PermissionManageChannelRoles); !ok {
return model.MakePermissionError(c.AppContext.Session(), []*model.Permission{model.PermissionManageChannelRoles})
}
}
return nil
}
func getGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
permissionErr := requireLicense(c)
if permissionErr != nil {