“Access Denied” para BreakRoleInheritance usando RunWithElevatedPrivileges delegate

Una de las últimas cosas en las que tuve que trabajar fue con sharepoint, al tratar de personalizar el panel de discusión para que se pudiera otorgar permisos a los usuarios que se seleccionaran mediante una webpart creada por un colega. No podía usuar la habilitacion de audiencia porque necesitaba usar un buscador personalizado, asi que la opción era programar mi propio campo audiencia y controlar los eventos (added-adding-adited-aditing) para que al crear un nuevo post o tema, se otorgaran los permisos a una audiencia determinada.

Problema:

Mi problema no era con el CustomFieldType si no que con el EventHandler que estaba controlando en donde uno de los pasos era remover todos los niveles de permisos sobre un SPListItem.
Mi requerimiento consistia en dar permisos solo a usuarios y grupos particulares sobre un item y quitar el resto.
El primer dolor de cabeza fue encontrar la forma de quitar todos los permisos sobre un item para luego darle los permisos sobre los usuarios y grupos que yo quisiera.
Hasta que encontre dos maneras de hacerlo:
Forma1:

for (int k = item.RoleAssignments.Count - 1; k >= 0; k--)
{
   item.RoleAssignments.Remove(k);
}

Forma2:

foreach (SPRoleAssignment spra in item.RoleAssignments) {
   spra.RoleDefinitionBindings.RemoveAll();
   spra.Update();
}

//donde item es un objeto SPListItem sobre el cual queremos remover los permisos
//por tema de rendimiento y optimizacion en el uso de recursos, la forma1 es recomendada

Mi segundo dolor de cabeza fue la exception que se generaba dentro de un método delegado que declaraba usando el método RunWithElevatedPrivileges() dentro de un EventHandler que habia generado para el evento ItemAdded() de un Panel de Discusión.
El código corria sin problemas cuando el evento ItemAdded() era generado por el administrador, pero levantaba una exception para cualquier otro usuario aún cuando esos otros usuarios tenian permisos de “Full Control” sobre la lista.
El problema principalmente era de permisos al ejecutar item.RoleAssignments.Remove(k) o spra.RoleDefinitionBindings.RemoveAll() ya que por mucho que definiera la ejecución con RunWithElevatedPrivileges() necesitaba manejar una serie de reglas para que todo funcionara bien.

Donde esta el problema:
Resulta que el método BreakRoleInheritance() resetea el parametro AllowUnsafeUpdates a False, del objeto SPWeb y de la propiedad Web del objeto SPListItem con lo cual obtienes un lindo security exception en tu cara.
Necesitas setear AllowUnsafeUpdates = true directamente despues de un BreakRoleInheritance, y especificamente, si es que puedes hacerlo, sobre la propiedad Web del SPListItem.
Algo super importante es que no uses ningun objeto creado fuera del RunWithElevatedPrivileges ya que los objetos SP tienen su propio security context. Trata de usar guids/urls/ids o lo que sea, pero siempre recrea los objetos dentro del metodo RunWithElevatedPrivileges.

Aca dejo los procedimientos y funciones usadas en mi solución:

Public Overrides Sub ItemAdded(ByVal properties As SPItemEventProperties)
	_properties = properties
	SPSecurity.RunWithElevatedPrivileges(New SPSecurity.CodeToRunElevated(AddressOf ManagePermissionItem))
End Sub
Public Sub ManagePermissionItem()
	Try
		Dim _webInUserContext As SPWeb = _properties.OpenWeb()
		Dim _siteInUserContext As SPSite = New SPSite(_properties.SiteId)
		Dim _itemInUserContext As SPListItem = _properties.ListItem
		Dim _webGuid As Guid = _webInUserContext.ID
		Dim _siteGuid As Guid = _siteInUserContext.ID
		Dim _itemGuid As Integer = _itemInUserContext.ID

		Dim _site As SPSite = New SPSite(_siteGuid)
		_site.AllowUnsafeUpdates = True
		Dim _web As SPWeb = _site.OpenWeb(_webGuid)
		_web.AllowUnsafeUpdates = True

		Dim _splist As SPList = _web.Lists("Lista1")
		Dim _item As SPListItem = _splist.GetItemById(_itemGuid)
		_item.Web.AllowUnsafeUpdates = True

		'Remover todos los permisos
		RemoveAllPermissions(_web, _item)
		_item.Web.AllowUnsafeUpdates = True

		'Obtener Usuarios y Grupos desde campo personalizado
		Dim itemValue As String = _item("Campo1").ToString()
		Dim splitter As String() = New String(0) {}
		Dim i As Integer = 0
		splitter(0) = ","
		If itemValue IsNot Nothing And itemValue  String.Empty Then
			Dim audiencia As String() = itemValue.Split(splitter, StringSplitOptions.RemoveEmptyEntries)
			For i = 0 To audiencia.Length - 1
				If True = audiencia(i).Contains("ldapmembership") Then 'Es un Usuario
					'Asignar pemisos con AddPermissionUser
					Dim user As SPUser = _web.AllUsers(audiencia(i))
					AddPermissionUser(_web, _item, user.ID, SPRoleType.Contributor)
				Else 'Es un Grupo
					'Asignar permisos con AddPermissionGroup
					AddPermissionGroup(_web, _item, audiencia(i), SPRoleType.Contributor)
				End If
			Next
			AddPermissionUser(_web, _item, _properties.CurrentUserId, SPRoleType.Contributor)
		End If
		_web.AllowUnsafeUpdates = False
	Catch generatedExceptionName As Exception

	End Try
End Sub
Public Sub AddPermissionUser(ByVal web As SPWeb, ByVal item As ISecurableObject, ByVal idUser As Integer, ByVal role As SPRoleType)
	Dim user As SPUser = web.Site.RootWeb.AllUsers.GetByID(idUser)
	Dim roleDefinition As SPRoleDefinition = web.RoleDefinitions.GetByType(role)
	Dim roleAssignment As SPRoleAssignment = Nothing

	If False = item.HasUniqueRoleAssignments Then
		item.BreakRoleInheritance(True)
		web.AllowUnsafeUpdates = True
	End If

	If TypeOf item Is SPListItem Then
		AddPermissionUser(web, DirectCast(item, SPListItem).ParentList, idUser, SPRoleType.Guest)
	ElseIf TypeOf item Is SPDocumentLibrary Then
		AddPermissionUser(web, DirectCast(item, SPDocumentLibrary).ParentWeb, idUser, SPRoleType.Guest)
	ElseIf TypeOf item Is SPList Then
		AddPermissionUser(web, DirectCast(item, SPList).ParentWeb, idUser, SPRoleType.Guest)
	End If

	Try
		roleAssignment = item.RoleAssignments.GetAssignmentByPrincipal(DirectCast(user, SPPrincipal))
	Catch generatedExceptionName As ArgumentException
		roleAssignment = New SPRoleAssignment(DirectCast(user, SPPrincipal))
	End Try
	Try
		roleAssignment.RoleDefinitionBindings.Add(roleDefinition)
		item.RoleAssignments.Add(roleAssignment)
	Catch generatedExceptionName As SPException
	End Try
	'Ahora Item posee el rol que deseamos asociarle.
End Sub
Public Sub AddPermissionGroup(ByVal web As SPWeb, ByVal item As ISecurableObject, ByVal idGroup As String, ByVal role As SPRoleType)
	Dim group As SPGroup = web.Site.RootWeb.SiteGroups(idGroup)
	Dim roleDefinition As SPRoleDefinition = web.RoleDefinitions.GetByType(role)
	Dim RoleAssignment As New SPRoleAssignment(group)

	If False = item.HasUniqueRoleAssignments Then
		item.BreakRoleInheritance(True)
		web.AllowUnsafeUpdates = True
	End If

	Try
		RoleAssignment.RoleDefinitionBindings.Add(roleDefinition)
		item.RoleAssignments.Add(RoleAssignment)
	Catch generatedExceptionName As SPException

	End Try

End Sub
Public Sub RemoveAllPermissions(ByVal web As SPWeb, ByVal item As ISecurableObject)
	Dim k As Integer = 0
	Try
		web.AllowUnsafeUpdates = True
		If False = item.HasUniqueRoleAssignments Then
			item.BreakRoleInheritance(True)
			web.AllowUnsafeUpdates = True
		End If

		For Each spra As SPRoleAssignment In item.RoleAssignments
			spra.RoleDefinitionBindings.RemoveAll()
			spra.Update()
		Next

	Catch generateExceptionName As SPException

	End Try
End Sub

About this entry