Asignación CAS de Sharepoint por contexto de usuario

Puesta en situación


En uno de nuestros proyectos hemos tenido que manejar el contexto del usuario en el negocio con el contexto de sharepoint. Consiste en que el cliente dispone de un conjunto de usuarios que están en la central, y otro dispersos por el territorio nacional, asociados en centros o delegaciones.  Una de las funcionalidades que disponía era la de enviar solicitudes desde los centros a la central. Lo que queríamos era que desde una misma página cada usuario solo viera las solicitudes creadas por los usuarios de su centro, y los gestores los de todos los centros.


La solución que pensamos fué la siguiente:


Cada solicitud tendría un CustomField con el centro que crea la solicitud. Este campo se encargaría de determinar el centro al que está asignado el usuario y asignar a la solicitud los permisos de seguridad correspondientes para que lo vieran solo los gestores y los usuarios del centro que lo creo. Para gestionar la seguridad creamos un grupo de seguridad para cada centro, y asignamos los usuarios al centro que le correspondía. Para relacionar Grupo de seguridad con Centro, utilizamos una lista personalizada con los centros disponibles.


Al crear una nueva solicitud, el CustomField detectaba el centro al que pertenecía el usuario comparando la lista de centros con los grupos de seguridad, y a continuación modificaba los permisos CAS del nuevo elemento.


 


Feature


Creamos una Feature de Wss3 para encapsular los componentes y asignar el EventReceiver a las listas al activarse la Feature.


 


CustomField de Wss3


Utilizamos un customField de tipo texto para almacenar el centro que crea la solicitud. Para que el usuario no tuviera que seleccionar su centro, quitamos el campo de los formularios de edición.


 


public class CentroCustomField : SPFieldText
    {
        ……………..


        /// <summary>
        /// Devuelve toda la lista de centros
        /// </summary>
        /// <returns></returns>
        private List<string> LoadAllCentros(){
            List<string> allCentros = new List<string>();
            try{
                SPList centrosList = SPContext.Current.Web.GetList(string.Format(«{0}/Lists/Centros», SPContext.Current.Web.Name));
                if(centrosList != null){
                    foreach(SPItem centro in centrosList.Items){
                        allCentros.Add((string)centro[«NombreCentro»]);
                    }
                }
            }catch(Exception ex){
            }
            return allCentros;
        }
      }


      ……………..


        protected override void CreateChildControls() {
            try{
              if (this.Field == null || this.ControlMode == SPControlMode.Display)
                return;
              base.CreateChildControls();


              this.CentroSelector = (DropDownList)TemplateContainer.FindControl(«CentroSelector»);

              if (this.CentroSelector == null)
                throw new Exception(«Control CentroFieldControl.ascx corrupto.»);

              if (!this.Page.IsPostBack)
              {
                  if (m_allCentros != null)
                  {
                      this.CentroSelector.Items.Add(» «);
                      this.CentroSelector.Items.Add(«Todos»);
                      foreach (string nombreCentro in m_allCentros)
                      {
                          this.CentroSelector.Items.Add(nombreCentro);
                      }
                  }
              }
            }catch(Exception ex){
            }
          }



……………..


    }


Event Handler


Para que nuestro campo supiera que centro debe asignar a la solicitud creamos un event receiver al agregar un elemento a nuestra lista de solicitudes.


Para registrar el event receiver utilizamos la activación de la feature:


 


public class FeatureReceiver : SPFeatureReceiver
   {

       public override void FeatureActivated(SPFeatureReceiverProperties properties)
       {
           try
           {
               SPWeb site = SPContext.Current.Web;

               string asmName = «FeatureCentros, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ea8da11d5c8d53be»;
               string itemReceiverName = «XXXXXXX.CentroEventReceiver»;

               SPList listaSolicitudes = site.Lists[«Solicitudes»];
               listaSolicitudes.EventReceivers.Add(SPEventReceiverType.ItemAdded,
                                             asmName,
                                             itemReceiverName);

               listaSolicitudes.Update();

           }
           catch { }
       }

}


Para que la feature sepa que debe ejecutar el Event Receiver indicamos en la feature


<Feature Id=»FE642B14-97FF-4a72-85F2-984FBD0A7D14″
   Title=»Herramientas de Centros»
   Description=»XXXXXXXXXXXXX»
   Version=»1.0.0.0″
   Scope=»Web»
   Hidden=»FALSE»
   ImageUrl=»XXXXXXX.jpg»
   ReceiverAssembly=»FeatureCentros, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ea8da11d5c8d53be»
   ReceiverClass=»XXXXXX.FeatureReceiver»
   xmlns=»http://schemas.microsoft.com/sharepoint/»>

  <ElementManifests>


………………………..
  </ElementManifests>


</Feature>

 


Event Receiver


Creamos un EventReceiver para que cuando se añada un elemento a la lista se apliquen los permisos correspondientes.


 


public class CentroEventReceiver : SPItemEventReceiver
    {

        public override void ItemAdded(SPItemEventProperties properties)
        {
            try
            {
                        if ((string)properties.ListItem[«Centro»] != «Todos»)
                        {
                            // Buscamos el centro asociado al usuario
                            SPUser usuario = properties.ListItem.ParentList.ParentWeb.CurrentUser;

                            using (SPWeb webOrigUser = properties.OpenWeb())
                            {
                                SPUserToken token = webOrigUser.AllUsers[«SHAREPOINT\system»].UserToken;
                                using (SPSite site = new SPSite(properties.SiteId, token))
                                {
                                    using (SPWeb currentWeb = site.OpenWeb(properties.RelativeWebUrl))
                                    {

                                        Dictionary<string, SPGroup> gruposAsociados = new Dictionary<string, SPGroup>();
                                        foreach (SPGroup grupo in usuario.Groups)
                                        {
                                            gruposAsociados.Add(grupo.Name, grupo);
                                        }

                                        List<SPGroup> centrosAsociados = new List<SPGroup>();
                                        SPList centrosList = currentWeb.GetList(string.Format(«{0}/Lists/Centros», currentWeb.Name));
                                        if (centrosList != null)
                                        {
                                            foreach (SPItem centro in centrosList.Items)
                                            {
                                                string nombreCentroLeido = (string)centro[«NombreCentro»];
                                                if (gruposAsociados.ContainsKey(nombreCentroLeido))
                                                {
                                                    centrosAsociados.Add(gruposAsociados[nombreCentroLeido]);
                                                }
                                            }
                                        }

                                        if (centrosAsociados.Count > 0)
                                        {
                                            try
                                            {
                                               DisableEventFiring();

                                                // Asignamos el primer centro al campo en caso de no ir relleno
                                                if ((string)properties.ListItem[«Centro»] == » «)
                                                {
                                                    properties.ListItem[«Centro»] = centrosAsociados[0].Name;
                                                }

                                                //// Asignamos los CAL que correspondan
                                                SPList listaAModificar = currentWeb.Lists[properties.ListId];
                                                SPListItem itemAModificar = listaAModificar.GetItemById(properties.ListItem.ID);
                                                if (!itemAModificar.HasUniqueRoleAssignments)
                                                    itemAModificar.BreakRoleInheritance(true);



                                                List<SPRoleAssignment> RoleABorrar = new List<SPRoleAssignment>();
                                                SPRoleAssignmentCollection roleAssignmentCollection = itemAModificar.RoleAssignments;
                                                foreach (SPRoleAssignment roleAssignment in roleAssignmentCollection)
                                                {
                                                    if (roleAssignment.Member.Name != «Gestores»                    &&
                                                        roleAssignment.Member.Name != «Consultores»                 &&
                                                        roleAssignment.Member.Name != «Propietarios eLogosPiloto»   &&
                                                        roleAssignment.Member.Name != «Cuenta del sistema»          &&
                                                        roleAssignment.Member.Name != centrosAsociados[0].Name      &&
                                                        roleAssignment.Member.Name != (string)properties.ListItem[«Centro»])
                                                    {
                                                        RoleABorrar.Add(roleAssignment);
                                                    }
                                                }
                                                foreach (SPRoleAssignment roleAssignment in RoleABorrar)
                                                    roleAssignmentCollection.Remove(roleAssignment.Member);


                                                itemAModificar.Update();

                                            }
                                            catch (Exception ex)
                                            {

                                            }
                                            finally
                                            {
                                                EnableEventFiring();
                                            }
                                        }
                                    }
                                }
                            }
                        }
               
            }
            catch (Exception ex)
            {
            }
        }

    }

Fijaros en algunos detalles:


SPUserToken token = webOrigUser.AllUsers[«SHAREPOINT\system»].UserToken;
using (SPSite site = new SPSite(properties.SiteId, token))
{
    using (SPWeb currentWeb = site.OpenWeb(properties.RelativeWebUrl))
    {


Este trozo de código hace que el código se ejecute con los permisos asociados al usuario «SHAREPOINTsystem» para que podamos realizar determinadas tareas de administración. Si no ejecutáramos nuestro código con privilegios al hacer un BreakRoleInheritance nos lanzaría una excepción del tipo  UnauthorizedAccessException.


El objetivo es que se ejecutara con permisos de administración, para ello podríamos haber utilizado un código similar al siguiente:


SPSecurity.RunWithElevatedPrivileges(delegate()

{

  SPUserToken token = webOrigUser.AllUsers[WindowsIdentity.GetCurrent().Name].UserToken;
  using (SPSite site = new SPSite(properties.SiteId, token))
  {
    using (SPWeb currentWeb = site.OpenWeb(properties.RelativeWebUrl))
     {


Esto funcionará siempre que tengamos activado en el fichero Web.config: <Identity Impersonate=”true”>


Fijaros en el código que estamos obteniendo una nueva referencia de SPSite, SPWeb y SPItem en lugar de utilizar las propiedades del parámetros del EventReceiver «SPItemEventProperties properties». Esto es debido a que necesitamos obtener las referencias al elemento que queremos modificar con las credenciales del usuario administrador.


 


DisableEventFiring: Permite que cualquier cambio que hagamos no provoque la ejecución de un workflow.

GetItemById: Hemos utilizado esta función en lugar de la colección Items, porque Items solo contiene los elementos disponibles antes de ejecutarse el evento y GetItemById es capaz de localizar el nuevo elemento.

BreakRoleInheritance: Rompe la herencia del contendor (la lista), para poder editar nuestros propios permisos. El valor True indica que copie todos los grupos que hereda de la lista, un valor False ´lo dejaría sin grupos. Un detalle, cada vez que utilicemos esta función tendremos que hacer un Dispose del Parent del elemento, tal y como se comenta en el blog de Roger Lamb’s

RoleAssignments: Es la colección de grupos de sharepoint asignados automáticamente al elemento.

Publicado por

Mario Cortés

Mario Cortés Flores es MVP en Office 365, trabaja en Plain Concepts como Team Lead y escribe habitualmente en geeks.ms/blogs/mcortes y en Twitter @mariocortesf. Podréis encontrarlo colaborando activamente con la comunidad de MadPoint y SUGES

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *