[release-10.11] MM-68547: Tighten authorization on group syncable link and patch endpoints (#36434)
Automatic Merge
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
977c791e5b
Коммит
202d125afa
@@ -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 {
|
||||
|
||||
Ссылка в новой задаче
Block a user