none
Consulta sobre Autenticación Windows + Roles de BDD local RRS feed

  • Pregunta

  • Buenas como están?

    Quisiera pedirles un favor, si puede orientarme respecto a crear una app.net mvc con autenticación de usuario de dominio AD y que los roles sean administrador por dicha aplicación ya que es una intranet. Utilizo Entity framework y mi modelo de capas es el siguiente:

    1)Data (entityFramework): acceso a datos de la aplicación y modelos de otras aplicaciones

    2)Services: Negocio

    3)ExternalServices: negocio con otras aplicaciones

    4)Contracts: definición de los contratos de los servicios anteriores

    5)MVC: projecto MVC modelo vista controlador

    6)Pruebas unitarias

    Estuve investigando en este enlace que está interesante, pero no usa usuarios de dominio AD, sino propio de la aplicación:

    https://anexsoft.com/roles-y-permisos-personalizados-con-asp-net-mvc

    Respecto a mi modelo es muy parecido al enlace anterior:

    Conocen algún hilo para poder seguir en español?

    Desde ya se agradece la respuesta.

    Saludos

    Rodri

    lunes, 11 de enero de 2021 14:04

Respuestas

  • Hola, entendí mal, muy mal y disculpas por ello. Lo que deseas es poder gestionar los roles de tu aplicativo. No?

    Vale, necesitas entonces un RoleProvider

    Para ello puedes implementar algo así

    public class CustomRoleProvider : RoleProvider
    {
    	public override string ApplicationName 
    	{ 
    	  get => throw new NotImplementedException(); 
    	  set => throw new NotImplementedException(); 
    	}
    
    	public override void AddUsersToRoles(string[] usernames, string[] roleNames)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override void CreateRole(string roleName)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override string[] FindUsersInRole(string roleName, string usernameToMatch)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override string[] GetAllRoles()
    	{
    		throw new NotImplementedException();
    	}
    
    	public override string[] GetRolesForUser(string username)
    	{
    		 using (SampleDBEntities objContext = new SampleDBEntities()) // Tu contexto de EF
    	         {
    		   var objUser = objContext.Users.FirstOrDefault(x => x.AppUserName == username);
    		   if (objUser == null)
    		   {
    			   return null;
    		   }
    		   else
    		   {
    			   return objUser.Roles.Select(x => x.RoleName).ToArray(); 
    		   }
    	   }
    	}
    
    	public override string[] GetUsersInRole(string roleName)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override bool IsUserInRole(string username, string roleName)
    	{
    		var roles = GetRolesForUser(username);
    
    		return roles.Contains(roleName,StringComparer.OrdinalIgnoreCase);
    	}
    
    	public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override bool RoleExists(string roleName)
    	{
    		throw new NotImplementedException();
    	}
    }
    

    Como ves implementas las operaciones requeridas de RoleProvider usando tu contexto de EF.

    Luego estableces en tu web.config el proveedor

    <roleManager cacheRolesInCookie="true" defaultProvider="CustomRoleProvider" enabled="true">
      <providers>
    	<clear />
    	<add name="CustomRoleProvider" type="[Namespace here].CustomRoleProvider, [Application Assembly here]" />
      </providers>
    </roleManager>


    También deberías implementar un atributo que herede de AuthorizationAttribute para gestionar los roles y permitir o no el acceso al método.

    Supongamos que tendrás los siguientes roles (así evitas las "magic string")

    public enum Roles
    {
    	User,
    	Developer,
    	Administrator
    }


    Implementamos el attribute

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public class RoleAuthorizeAttribute : AuthorizeAttribute
    {
    	public RoleAuthorizeAttribute(params object[] roles)
    	{
    		if (roles.Any(r => r.GetType().BaseType != typeof(Enum)))
    			throw new ArgumentException("The roles parameter may only contain enums", "roles");
    
    		var temp = roles.Select(r => Enum.GetName(r.GetType(), r)).ToList(); 
    		Roles = string.Join(",", temp);
    	}
    
    	public override void OnAuthorization(AuthorizationContext filterContext)
    	{
    		var request = filterContext.HttpContext.Request; 
    		var url = new UrlHelper(filterContext.RequestContext); 
    		var accessDeniedUrl = url.Action("AccessDenied", "Error"); 
    
    		if (!string.IsNullOrEmpty(base.Roles))
    		{
    			var isRoleError = true;
    			var rolesAllowed = base.Roles.Split(',');
    
    			var user = filterContext.HttpContext.User;
    			if (user != null && rolesAllowed.Any())
    			{
    				foreach (var role in rolesAllowed)
    					if (user.IsInRole(role))
    						isRoleError = false;
    			}
    
    			if (isRoleError)
    			{
    				if (request.IsAjaxRequest())
    					filterContext.Result = new JsonResult { Data = new { error = true, signinerror = true, message = "Access denied", url = accessDeniedUrl }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
    				else
    					filterContext.Result = new RedirectResult(accessDeniedUrl);
    			} 
    		}
    	}
    } 

    Y para usarlo en tu métdo de acción o en tu controlador

    [RoleAuthorize(Roles.User, Roles.Developer)]
    public ActionResult Index()
    {
    }


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    lunes, 11 de enero de 2021 17:21
    Moderador
  • Si, efectivamente esa era otra opción que iba a preguntarte. Para ello deberías desde tu aplicación conectar con el AD y gestionar las cosas empleando System.DirectoryServices, el cual usa un PrincipalContext de una cuenta con permisos de Admin en tu AD. Básicamente en tu RoleProvider en vez de usar tu context de EF usarías las herramientas de DirectoryServices

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    martes, 12 de enero de 2021 13:50
    Moderador
  • Enlaces interesantes

    http://slalomdev.blogspot.com/2008/08/active-directory-role-provider.html?m=1

    https://masudprogrammer.wordpress.com/2012/11/21/active-directory-role-provider-with-asp-net-c/


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    martes, 12 de enero de 2021 13:52
    Moderador

Todas las respuestas

  • modelo mencionado anteriormente
    lunes, 11 de enero de 2021 14:05
  • Hola, si los roles los gestiona Azure Ad, éstos vienen en el token de autorización que devuelve.

    Aquí tienes un post sobre ello


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    lunes, 11 de enero de 2021 15:02
    Moderador
  • Hola, no los gestiona Azure AD, es un dominio local. Gestionado por nosotros mismos.
    • Editado marozzir lunes, 11 de enero de 2021 16:02
    lunes, 11 de enero de 2021 16:01
  • Hola, entendí mal, muy mal y disculpas por ello. Lo que deseas es poder gestionar los roles de tu aplicativo. No?

    Vale, necesitas entonces un RoleProvider

    Para ello puedes implementar algo así

    public class CustomRoleProvider : RoleProvider
    {
    	public override string ApplicationName 
    	{ 
    	  get => throw new NotImplementedException(); 
    	  set => throw new NotImplementedException(); 
    	}
    
    	public override void AddUsersToRoles(string[] usernames, string[] roleNames)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override void CreateRole(string roleName)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override string[] FindUsersInRole(string roleName, string usernameToMatch)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override string[] GetAllRoles()
    	{
    		throw new NotImplementedException();
    	}
    
    	public override string[] GetRolesForUser(string username)
    	{
    		 using (SampleDBEntities objContext = new SampleDBEntities()) // Tu contexto de EF
    	         {
    		   var objUser = objContext.Users.FirstOrDefault(x => x.AppUserName == username);
    		   if (objUser == null)
    		   {
    			   return null;
    		   }
    		   else
    		   {
    			   return objUser.Roles.Select(x => x.RoleName).ToArray(); 
    		   }
    	   }
    	}
    
    	public override string[] GetUsersInRole(string roleName)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override bool IsUserInRole(string username, string roleName)
    	{
    		var roles = GetRolesForUser(username);
    
    		return roles.Contains(roleName,StringComparer.OrdinalIgnoreCase);
    	}
    
    	public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override bool RoleExists(string roleName)
    	{
    		throw new NotImplementedException();
    	}
    }
    

    Como ves implementas las operaciones requeridas de RoleProvider usando tu contexto de EF.

    Luego estableces en tu web.config el proveedor

    <roleManager cacheRolesInCookie="true" defaultProvider="CustomRoleProvider" enabled="true">
      <providers>
    	<clear />
    	<add name="CustomRoleProvider" type="[Namespace here].CustomRoleProvider, [Application Assembly here]" />
      </providers>
    </roleManager>


    También deberías implementar un atributo que herede de AuthorizationAttribute para gestionar los roles y permitir o no el acceso al método.

    Supongamos que tendrás los siguientes roles (así evitas las "magic string")

    public enum Roles
    {
    	User,
    	Developer,
    	Administrator
    }


    Implementamos el attribute

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public class RoleAuthorizeAttribute : AuthorizeAttribute
    {
    	public RoleAuthorizeAttribute(params object[] roles)
    	{
    		if (roles.Any(r => r.GetType().BaseType != typeof(Enum)))
    			throw new ArgumentException("The roles parameter may only contain enums", "roles");
    
    		var temp = roles.Select(r => Enum.GetName(r.GetType(), r)).ToList(); 
    		Roles = string.Join(",", temp);
    	}
    
    	public override void OnAuthorization(AuthorizationContext filterContext)
    	{
    		var request = filterContext.HttpContext.Request; 
    		var url = new UrlHelper(filterContext.RequestContext); 
    		var accessDeniedUrl = url.Action("AccessDenied", "Error"); 
    
    		if (!string.IsNullOrEmpty(base.Roles))
    		{
    			var isRoleError = true;
    			var rolesAllowed = base.Roles.Split(',');
    
    			var user = filterContext.HttpContext.User;
    			if (user != null && rolesAllowed.Any())
    			{
    				foreach (var role in rolesAllowed)
    					if (user.IsInRole(role))
    						isRoleError = false;
    			}
    
    			if (isRoleError)
    			{
    				if (request.IsAjaxRequest())
    					filterContext.Result = new JsonResult { Data = new { error = true, signinerror = true, message = "Access denied", url = accessDeniedUrl }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
    				else
    					filterContext.Result = new RedirectResult(accessDeniedUrl);
    			} 
    		}
    	}
    } 

    Y para usarlo en tu métdo de acción o en tu controlador

    [RoleAuthorize(Roles.User, Roles.Developer)]
    public ActionResult Index()
    {
    }


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    lunes, 11 de enero de 2021 17:21
    Moderador
  • Hola,

    Gracias por levantar tu consulta en los foros de MSDN.

    Eric Ruiz

    ____________________________

    Por favor recuerde "Marcar como respuesta" las respuestas que hayan resuelto su problema, es una forma común de reconocer a aquellos que han ayudado, y hace que sea más fácil para los otros visitantes encontrar la solución más tarde.

    Si tiene algún cumplido o reclamo sobre el soporte de MSDN siéntase en la libertad de contactar MSDNFSF@microsoft.com.

    lunes, 11 de enero de 2021 17:29
    Moderador
  • Muchas gracias Sergio, voy a trabajar en ello ahora que me encaminas.
    lunes, 11 de enero de 2021 18:36
  • Hola Sergio, ayer estuvimos analizando tu respuesta y me dejó muy conforme. Esto es en el caso de querer administrar los roles dentro de la aplicación.

    En caso de querer manejar los roles a través de un grupo de Active Directory (esto es asignando un usuario del dominio a un grupo de AD, y que la aplicación asigne las funciones correspondientes de acuerdo a ello, posees algo de información para realizar eso?

    En este caso minimizaría el diseño de la aplicación web.

    Disculpas por las molestias.

    Muchas gracias.

    Ss.

    Rodrigo

    martes, 12 de enero de 2021 11:35
  • Si, efectivamente esa era otra opción que iba a preguntarte. Para ello deberías desde tu aplicación conectar con el AD y gestionar las cosas empleando System.DirectoryServices, el cual usa un PrincipalContext de una cuenta con permisos de Admin en tu AD. Básicamente en tu RoleProvider en vez de usar tu context de EF usarías las herramientas de DirectoryServices

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    martes, 12 de enero de 2021 13:50
    Moderador
  • Enlaces interesantes

    http://slalomdev.blogspot.com/2008/08/active-directory-role-provider.html?m=1

    https://masudprogrammer.wordpress.com/2012/11/21/active-directory-role-provider-with-asp-net-c/


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    martes, 12 de enero de 2021 13:52
    Moderador
  • Gracias, hoy analizaré esta propuesta.
    martes, 12 de enero de 2021 13:54
  • Ya creamos el grupo de seguridad en AD. Luego asigné mi usuario DOMINIO\USER en el grupo anterior, agregué lo siguiente en el web.config:

    <system.web>
        <compilation debug="true" targetFramework="4.7.2" />
        <httpRuntime targetFramework="4.7.2" />
        <authentication mode="Windows" />
        <authorization>
          <deny users="?" />
        </authorization>
        <roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
          <providers>
            <clear />
            <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
          </providers>
        </roleManager>
      </system.web>
      <runtime>

    Luego agregué en el HomeController la autorización al controlador Index (Authorize(Roles = @"DOMINIO\GRUPOAD") y anduvo, además probé en el about y contact, también anduvo. Me resultó raro que pida usuario y contraseña en cada link. Le di guardar contraseña y ya no lo pide más, pero creo que no debería hacerlo, ya que el usuario no debe recibirlo.

    Authorize(Roles = @"DOMINIO\GRUPOAD"

    Ahora debería asignar las autorizaciones a cada controlador? Eso es todo? o me está faltando algo?

    SS.

    miércoles, 13 de enero de 2021 12:58
  • Hola, si el atributo lo tienes escrito a nivel de clase (controller) en cada petición a ese controlador, va a chequear la autorización. Colocarlo en los métodos de acción que necesiten ser autenticados es suficiente. Lo de cada link no te entiendo, puedes mostrar algo de código? Esos links dónde apuntan?


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    miércoles, 13 de enero de 2021 15:19
    Moderador
  • Hola, todo bien? Se puede cerrar la pregunta? Gracias

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    jueves, 14 de enero de 2021 11:51
    Moderador