Por defecto, en un proyecto MVC 5 de Visual Studio 2013, la base de datos que Entity Framework utilizada es LocalDb. Con este tip, podremos cambiar a una conección diferente de Base de Datos, por medio del archivo de configuración.
Solo debemos reemplazar la siguiente sección del archivo Web.config. (Configurado para LocalDB)
Espero poder terminar una animación que he estado realizando de hace algunas semanas. Dejo una imagen para que puedan ver de qué trata. Me gustaría dar un mensaje de que Jesús ya regresa, de que los tiempo son cortos, y de que debemos guardar todos los 10 mandamientos.
La escena fue hecha en Maya 2012 y renderizado con Mental Ray. Espero tener dentro de poco noticias de mi avance. Si me pudieran ayudar a compartir estas imágenes, se los agredecería.
Cómo me impresionan las aves... Ingeniería increíble, hecho para deleitar los sentidos y hacer volar la imaginación. Gracias al Creador y Señor Jesús por semejante creació. Dejo un dibujo a lápiz 6B, sobre croquis grueso, el cual hice el otro día. Y un rápido retrato de mi esposita.
He finalizado por fin una imagen que he querido hacer desde hace mucho. La he realizado con Autodesk Maya 2012, y Arnold Render. Y algunos retoques con con Photoshop.
En mi último proyecto de desarrollo de mi trabajo, he podido utilizar las bondades de Active Dictory (desde ahora AD), para poder dar acceso a usuarios y grupo de usuarios específicos. Esto es muy útil ya que si debemos controlar qué persona entrará a nuestra página Web, y si además nuestra empresa y sus redes están utilizando Servicios de Directorios LDAP como Active Dictory, entonces esta herramienta de programación será lo ideal.
A continuación muestro cómo he aplicado esta funcionalidad sobre una aplicación Web Asp.net, hecha con C#. A este método se le domina: Autentificación de Formularios a través de Active Dictory, y me baso en el artículo de microsoft http://support.microsoft.com/kb/316748/es eso si con algunas modificaciones.
En resumen, los contenidos de documento son:
1) Crear clase LdapAuthentication.cs.
2) Agregar código al archivo global.asax.
3) Agregar código al archivo Web.config.
4) Crear la página Logon.aspx.
5) Agregar código personalizado a Logon.aspx.cs
6)Habilitando el acceso a los diferentes formulario del Software.
7) Configuraciones faltantes en mi desarrollo.
Autentificación de Formularios a través de Active Dictory
1.- Crear clase LdapAuthentication.cs
Lo primero es crear una clase, la cual se sugiere que el nombre sea 'LdapAuthentication.cs'. Esta es, por así decirlo, la clase principal, la cual contiene los métodos necesarios para poder acceder a AD. En resumen, se le entregan tres parámetros: dominio, usuario y contraseña. El método 'IsAuthenticated()' averigua si usuario y contraseña son correctos y el método 'GetGroups()' obtiene el o los grupos al cual pertenece este usuario. El código es el siguiente:
using System;
using System.Text;
using System.Collections;
using System.DirectoryServices;
namespace FormsAuth
{
public class LdapAuthentication
{
private String _path;
private String _filterAttribute;
private String _username;
DirectoryEntry _entry; //modi
public LdapAuthentication(String path)
{
_path = path;
}
public bool IsAuthenticated(String domain, String username, String pwd)
{
string domainAndUsername = domain + @"\" + username;
_username = username;
// string domainAndUsername = "ADUNACH\\usuario1";
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
_entry = entry;
try
{ //Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error al autenticar usuario" + ex.Message);
}
return true;
}
public String GetGroups()
{
DirectorySearcher search = new DirectorySearcher(_entry);
search.Filter = "(cn=" + _filterAttribute + ")";
//search.Filter = "samAccountName=" + _username;
search.PropertiesToLoad.Add("memberOf");
StringBuilder groupNames = new StringBuilder();
try
{
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
String dn;
int equalsIndex, commaIndex;
for (int propertyCounter = 0; propertyCounter < propertyCount; propertyCounter++)
{
dn = (String)result.Properties["memberOf"][propertyCounter];
equalsIndex = dn.IndexOf("=", 1);
commaIndex = dn.IndexOf(",", 1);
if (-1 == equalsIndex)
{
return null;
}
groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1));
groupNames.Append("|");
}
}
catch (Exception ex)
{
throw new Exception("Error obtaining group names. " + ex.Message);
}
return groupNames.ToString();
}
}
}
2.- Agregar código al archivo global.asax
El archivo global.asax es un controlador de eventos. Interactúa con las Cookies, y junto con el archivo 'LdapAuthentication.cs' averigua la lista de grupos. El siguiente es el código, y además le he agregado un pequeño código el cual guardará en una cookie, los grupos que contiene el usuario, y el nombre del usuario será el nombre de la cookie. El siguiente método deberá reemplazar al que ya existe:
void Application_AuthenticateRequest(Object sender, EventArgs e)
{
String cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (null == authCookie)
{//There is no authentication cookie.
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch (Exception ex)
{
//Write the exception to the Event Log.
return;
}
if (null == authTicket)
{//Cookie failed to decrypt.
return;
}
//When the ticket was created, the UserData property was assigned a
//pipe-delimited string of group names.
String[] groups = authTicket.UserData.Split(new char[] { '|' });
//Create an Identity.
GenericIdentity id = new GenericIdentity(authTicket.Name, "LdapAuthentication");
//This principal flows throughout the request.
GenericPrincipal principal = new GenericPrincipal(id, groups);
//----Modificacion Marlon-----
//--Guardo datos: nombre usuario y grupos que tiene
string nombreCookie = "caunach-" + id.Name;
string valor = "";
foreach (string a in groups)
{
valor = valor +a +"?";
}
Response.Cookies.Remove(nombreCookie);
Response.Cookies[nombreCookie].Value = valor;
//-------- Fin Modif Marlon --------------
Context.User = principal;
}
3.- Modificando el archivo Web.confg
El siguiente extracto de código debe colocarse después de , e indicará cual será la forma de autentificación, en nuestro caso por Formularios. También indica cual será la página que servirá para el ingreso de las credenciales y cual será la cookie que controla y contiene el acceso. Tanto el acceso y el control de duración de la sessión son guardadas en la cookie 'adAuthCookie'.
4.- Crear la página Logon.aspx
Esta contiene los TextBox necesarios para ingresar tanto usuario como contraseña. Además contiene el código necesario para interactuar con las clases y métodos ya creados, y ver si puede continuar y acceder a otras partes del programa. Si el ingreso de las credenciales son erroneos, esta pantalla (logon.aspx), no permitirá el acceso a otra página. Cito un texto de la documentación de Microsoft:
'La página Logon.aspx es una página que recopila la información de los métodos de usuario y la llamada en la clase LdapAuthentication . Después el código autentica al usuario y obtiene una lista de grupos, el código crea un objeto FormsAuthenticationTicket , cifra el vale, agrega el vale cifrado a una cookie, agrega la cookie a la colección HttpResponse.Cookies y, a continuación, redirige la solicitud a la dirección URL que se solicitó originalmente.'
El código es el siguiente, notar que es solo de Body hacia abajo lo que yo pongo:
5.- Agrego código personalizado al archivo Logon.aspx.cs
Debo escribir algunas lineas de código en este archivo, para obtener el valor de DOMINIO. Me explico, al comienzo explicamos que la clase principal LdapAuthentication.cs, y por tanto toda estas herramientas, trabajan con tres parametros ingresados: Usuario, contraseña y dominio. Entonces sabemos que tanto 'Usuario' como 'Contraseña' son elementos que ingresaran y constantemente y no necesariamente serán siempre los mismos, pero no pasa así con 'Dominio'. El dominio, prácticamente no cambiará, ya que generalmente habrá un solo dominio para una institución (ej iis.com, APPCEE, origin.com, etc). En nuestro caso hay uno solo, y no lo escribo en código, sino lo guardo cómo un valor en Base de Datos. Agrego entonces el código que me permite extraer este Dominio.
---------------
protected void Page_Load(object sender, EventArgs e)
{
dominio = obtenerConfiguracion("DOM");
//traigo el usuario de autoservicio
if (IsPostBack != true)
{
txtUsername.Text = Request.QueryString["usr"];
}
}
Notar que obtenerConfiguracion("DOM") es mi método que permite extraer el dominio de una tabla en base de datos. Por ejemplo, un dominio puede ser 'inacap.com', 'itt.org' etc.
6.- Habilitando el acceso a los diferentes formulario del software
Este código está basado en la documentación de Microsoft, dirección mencionada arriba, pero los que pongo en este documento ya están alterados y han sufridos algunas modificaciones, para el buen funcionamiento de mi aplicación.
Ahora que tenemos todo esto funcionando quiero mostrar cómo se va a dar acceso o denegar acceso (programáticamente) el ingreso a los formularios, una vez que tenemos el resultado de la autentificación.
Este resultado se almacena en una variable global llamada 'Context'. En mi aplicación yo obtengo el nombre de usuario que se ha logueado y autorizado correctamente a través de 'Context.User.identity.Name'. Y también debo obtener el o los grupos de AD a los cuales este usuario pertenece. Pero, aunque la variable Context tiene esta información, esta es privada, por lo que no puedo extraerla. Así que la recupero de una Cookie que hice en el archivo Global.asax.cs (visto más arriba). Entonces, el código para obtener el usuario y los grupos de este usuario, y además darle acceso al usuario ya autenticado es el siguiente, y puede ir en cualquier Forms que exista, dentro del método Page_load()
//Primero de todo veo si el usuario está logeado
//y ademas pertenece al grupo de "FACULTAD"
int encontrado = 0;
string usuario = Context.User.Identity.Name;
string grupos = "";
string aa = Context.User.Identity.AuthenticationType[0].ToString();
if (Request.Cookies["caunach-" + usuario] != null)
{
grupos = Server.HtmlEncode(Request.Cookies["caunach-" + usuario].Value);
string[] grup = grupos.Split('?');
foreach (string g in grup)
{
if (g.Trim().Equals("facultad"))
{
encontrado = 1;
}
}
}
if (encontrado == 0)
{
Response.Redirect("../AccesoNoAutorizado.aspx");
}
En resumen, obtengo usuario de 'Context', con esto entonces (ya que mi cookie de grupos tiene el nombre del usuario), averiguo los grupos de la cookie. Pregunto si este usuario pertenece a un grupo llamado 'facultad', y si es así, puedo continuar cargando este formulario normalmente, si no (no pertenece a grupo 'facultad'), lo redirecciono inmediatamente a una página de donde se le menciona que no tiene autorización para ver este contenido.
7.- Configuraciónes faltantes en mi desarrollo
Me he saltado algunas configuraciones, debido a que no ha sido necesario o ya estaban pre-establecidos en mis herramientas de desarrollo. Por ejemplo la opción de configuración del archivo Web.config: (del apartado 'Modificar el archivo Web.config' del tutorial de Microsoft) no la he habilitado. Así también el apartado 'Configurar IIS para la autenticación anónima', ya estaba configurada en mi servidor IIS.