Introducción
De seguro te ha tocado entrar a sitios web en donde cierta parte del sitio está restringida para usuarios con contraseña. Hay diversas maneras de lograr esto, unos hacen el sistema desde cero, implementando ellos mismos las sesiones, chequeo de contraseñas, etc.
Nosotros lo que vamos a hacer es apoyarnos en el .htaccess y el .htpasswd para lograr esto, así cuando nuestros usuarios quieran entrar a un directorio protegido, les saldrá una ventana pidiendo su username y contraseña.
Aparte de ello vamos a crear un sencillo sistema de administración, con el cual vamos a poder dar de alta y de baja usuarios.
Estoy suponiendo que al leer el tutorial ya tienes conceptos básicos de como leer arhivos, recibir datos en tu CGI y desplegar HTML con tus scripts.
El .htpasswd
El .htpasswd es un archivo de texto sencillo, en el cual en cada línea tenemos la entrada de los usuarios y su contraseñas con la siguiente sintáxis:
[username]:[contraseña_codificada]
Vemos como el separador son los dos puntos :, y cabe notar que las contraseñas deben de ir codificadas, eso lo veremos más adelante.
El .htpasswd lo vamos a usar para indicarle a nuestro servidor donde es que tiene que buscar la combinación usuario-contraseña que tienen acceso a cierto directorio.
Vamos a crear nuestro directorio protegido al cual vamos a llamar miembros, el directorio lo creamos dentro de la raíz de nuestro sitio web, de forma que podamos accesarlo de la siguiente manera: http://misitio.com/miembros/
Ya que tengamos el directorio creamos el archivo y lo guardamos dentro de nuestro directorio de miembros, ahorita no le vamos a poner nada, pues eso lo haremos más adelante con nuestro sistema de administración.
El .htaccess
El .htaccess o Hypertext Access es un archivo que contiene directivas que le indica a Apache la forma en que debe de comportarse en ciertos directorios.
Creamos un nuevo archivo .htaccess y lo guardamos dentro del directorio miembros.
Ahora vamos a configurar las directivas de nuestro archivo para proteger nuestro directorio con contraseña.
AuthUserFile /home/uriel/public_html/miembros/.htpasswd
AuthType Basic
AuthName "Seccion de Miembros"
require valid-user
La primera línea indica cual es el archivo .htpasswd que queremos usar, aquí debemos de poner la ruta absoluta a nuestro archivo .htpasswd que acabamos de crear dentro del directorio miembros.
Puedes checar de manera sencilla cual es la ruta absoluta usando las variables de entorno.
La segunda línea inidica que tipo de identificación queremos que se haga, en la tercera línea podemos asignarle un nombre de nuestro directorio protegido, en este caso le puse "Seccion Miembros".
La última línea es la que hace todo trabajar, pues indica que es necesario que haya un user válido para poder accesar. Un user válido es aquel que puso un username y contraseña igual a cualquiera de nuestras entradas dentro de .htpasswd.
Guarda el archivo e intenta entrar a tu directorio con un navegador. Si todo funciona bien te debe de aparecer un cuadro pidiendote tu username y contraseña.
Administración de Usuarios
Muy bien, ya tenemos configurado nuestro directorio, ahora vamos a crear nuestro sencillo sistema de administración por medio del cual podemos agregar y eliminar usuarios de manera sencilla.
Lo primero que vamos a hacer es la parte encargada de crear nuevos usuarios. Vamos a crear nuestro CGI al cual vamos a llamar misusuarios.cgi.
Para nuestro sistema de administración, vamos a habilitar los warnings, el pragma strict y vamos a usar el módulo CGI:
#!/usr/bin/perl
use warnings;
use strict;
use CGI;
Como vamos a estar mandando campos a nuestro formulario, vamos a usar el módulo CGI para leer los campos recibidos y guardarlos en unas variables para que podamos usarlos:
my $query = new CGI;
my %Input = $query->Vars;
Ahora vamos a crear nuestras variables globales:
my $cgiurl = "$ENV{'SCRIPT_NAME'}";
my $htpasswd = '/home/uriel/public_html/miembros/.htpasswd';
La primer variable nos da el URL a nuestro script usando una variable de entorno, lo cual vamos a usar para hacer el POST de manera correcta en nuestros formularios, y la segunda es la ruta absoluta al .htpasswd que queremos administrar.
Después lo que vamos a hacer es crear una nueva subrutina que va a imprimir una tabla con los campos necesarios para crear un nuevo usuario, la subrutina la ponemos al final de nuestro script:
sub tabla_nuevo_usuario{
print qq|
<h1>Crear Nuevo Username</h1>
<form action="$cgiurl" method="post">
<table cellpadding="5" cellspacing="5">
<tr>
<td align="right"><b>Username:</b></td>
<td align="left"><input type="text" name="username" size="20"></td>
</tr>
<tr>
<td align="right"><b>Contraseña:</b></td>
<td align="left"><input type="text" name="pwd" size="20"></td>
</tr>
<tr>
<td align="right"></td>
<td align="left"><input type="submit" name="crear_username" value=" Crear Username "></td>
</tr>
</table>
</form>
<hr>
|;
}
Ahora después vamos a poner la llamada a la nueva subrutina:
#!/usr/bin/perl
use warnings;
use strict;
use CGI;
my $cgiurl = "$ENV{'SCRIPT_NAME'}";
my $htpasswd = '/home/uriel/public_html/miembros/.htpasswd';
print "Content-type: text/html\n\n";
tabla_nuevo_usuario();
Si en este momento ejecutamos nuestro script vemos que simplemente imprime un formulario para crear un nuevo usuario pero realmente no hace nada.
Ahora lo que vamos a hacer es crear una subrutina al final de nuestro archivo que va a crear la nueva entrada en nuestro archivo .htpasswd con los datos recibidos:
sub crear_nuevo_usuario{
open my $file, '>>', $htpasswd or die("No se pudo abrir el .htpasswd: $!");
print {$file} join ":", $Input{'username'}, crypt($Input{'pwd'}, "aa");
print {$file} "\n";
close $file;
}
La subrutina es sencilla, abre el archivo .htpasswd para escritura, en la línea:
print {$file} join ":", $Input{'username'}, crypt($Input{'pwd'}, "aa");
Estamos uniendo los campos que recibimos con el separador ":", el username se guarda como se recibió, pero la contraseña la debemos de encriptar usando la función crypt() de Perl.
Después damos fin de línea imprimiendo \n en nuestro archivo.
Ahora, tenemos que llamar a la función cuando sea necesario, entonces en la parte superior de nuestro script en donde estamos poniendo las llamadas de nuestras subrutinas vamos a poner:
print "Content-type: text/html\n\n";
if(defined($Input{'crear_username'})){
crear_nuevo_usuario();
}
tabla_nuevo_usuario();
Si vemos en el formulario para crear el nuevo usuario, el botón de submit tiene el nombre de "crear_username", entonces lo que hacemos es ver si hemos recibido el campo, de ser así entonces llamamos a la subrutina "crear_nuevo_usuario();"
Ahora probemos lo que hemos hecho, entra a tu CGI de administración y crea un nuevo username con la contraseña que quieras.
Después ve a tu directorio protegido e intenta ingresar con el username y contraseña que acabas de crear, recuerda que tanto el username como la contraseña son sensibles a mayúsculas. Si lograste accesar es que tienes todo bien configurado.
Eliminando Usuarios
Ya tenemos la capacidad de agregar usuarios, ahora vamos a poner la capacidad de poder quitar usuarios. Lo primero que vamos a hacer es crear una subrutina al final de nuestro script que enliste todos nuestros usuarios, para poder seleccionar el que queremos eliminar:
sub enlistar_usuarios{
print qq|
<h1>Usuarios Activos</h1>
<form action="$cgiurl" method="post">
<table cellpadding="5" cellspacing="5">
<tr>
<td>
<select name="usuario" size="10">
|;
open my $file, '<', $htpasswd or die("No se pudo abrir .htpasswd: $!");
while(my $line = <$file>){
chomp($line);
my ($usuario, $pwd) = split(/\:/, $line);
print "<option value=\"$line\">$usuario</option>\n";
}
close $file;
print qq|
</td>
<td>
<input type="submit" name="eliminar_username" value=" Eliminar Usuario" >
</td>
</tr>
</table>
</form>
<hr>
|;
}
Lo más complicado de la subrutina es la siguiente parte:
1: open my $file, '<', $htpasswd or die("No se pudo abrir .htpasswd: $!");
2: while(my $line = <$file>){
3: chomp($line);
4: my ($usuario, $pwd) = split(/\:/, $line);
5: print "<option value=\"$line\">$usuario</option>\n";
6: }
7: close $file;
1 Abrimos el archivo .htpasswd como pura lectura.
2 Hacemos un bucle while para leer cada línea de nuestro archivo, creando una variable temporal $line con el contenido de cada línea.
3 Quitamos el carácter \n final.
4 Dividimos la línea usando el separador ":" y guardamos cada parte en la variable $usuario y $pwd.
5 Imprimimos un nuevo option.
7 Cerramos el archivo.
Ahora, en donde hacemos las llamadas de nuestras subrutinas, vamos a llamar a nuestra nueva subrutina:
print "Content-type: text/html\n\n";
if(defined($Input{'crear_username'})){
crear_nuevo_usuario();
}
tabla_nuevo_usuario();
enlistar_usuarios();
Muy bien, ahora si ejecutamos nuestro script, vemos como nos imprime la lista de los usuarios que actualmente tienen acceso por medio del .htpasswd.
Ahora, vamos a crear la subrutina para eliminar el usuario y lo ponemos al final de nuestro script:
sub eliminar_usuario{
open my $in, '<', $htpasswd or die("No se pudo leer archivo .htpasswd: $!");
my @usuarios = <$in>;
close $in;
open my $out, '>', $htpasswd or die("No se pudo leer archivo .htpasswd: $!");
for my $usuario(@usuarios){
chomp($usuario);
if($usuario ne $Input{'usuario'}){
print {$out} $usuario, "\n";
}
}
close ($out);
}
Quizá ésta sea la parte más complicada de todo el script, vamos a checarlo línea por línea:
1: open my $in, '<', $htpasswd or die("No se pudo leer archivo .htpasswd: $!");
2: my @usuarios = <$in>;
3: close $in;
4:
5:
6: open my $out, '>', $htpasswd or die("No se pudo leer archivo .htpasswd: $!");
7:
8: for my $usuario(@usuarios){
9: chomp($usuario);
10:
11: if($usuario ne $Input{'usuario'}){
12: print {$out} $usuario, "\n";
13: }
14:
15: }
16:
17: close ($out);
De la línea 1 a la 3 leemos nuestro archivo .htpasswd y guardamos su contenido en nuestro array @usuarios.
6 Abrimos el archivo para escritura, remplazando el contenido que en él había.
De la línea 8 a la 15 creamos un bucle a través de todo nuestro arreglo.
11 Checamos si la línea es igual a la que recibimos, de ser así entonces no la volvemos a poner en el archivo.
Finalemente ponemos la llamada de la función en caso de que sea necesario al principio de nuestro script:
print "Content-type: text/html\n\n";
if(defined($Input{'crear_username'})){
crear_nuevo_usuario();
}
if(defined($Input{'eliminar_username'})){
eliminar_usuario();
}
tabla_nuevo_usuario();
enlistar_usuarios();
Ahora puedes probar tu nuevo script, ya puedes quitar y poner nuevos usuarios.
Código Final
#!/usr/bin/perl
use warnings;
use strict;
use CGI;
my $query = new CGI;
my %Input = $query->Vars;
my $cgiurl = "$ENV{'SCRIPT_NAME'}";
my $htpasswd = '/home/baboonso/www/perlenespanol/miembros/.htpasswd';
print "Content-type: text/html\n\n";
if(defined($Input{'crear_username'})){
crear_nuevo_usuario();
}
if(defined($Input{'eliminar_username'})){
eliminar_usuario();
}
tabla_nuevo_usuario();
enlistar_usuarios();
sub tabla_nuevo_usuario{
print qq|
<h1>Crear Nuevo Username</h1>
<form action="$cgiurl" method="post">
<table cellpadding="5" cellspacing="5">
<tr>
<td align="right"><b>Username:</b></td>
<td align="left"><input type="text" name="username" size="20"></td>
</tr>
<tr>
<td align="right"><b>Contraseña:</b></td>
<td align="left"><input type="text" name="pwd" size="20"></td>
</tr>
<tr>
<td align="right"></td>
<td align="left"><input type="submit" name="crear_username" value=" Crear Username "></td>
</tr>
</table>
</form>
<hr>
|;
}
sub crear_nuevo_usuario{
open my $file, '>>', $htpasswd or die("No se pudo abrir el .htpasswd: $!");
print {$file} join ":", $Input{'username'}, crypt($Input{'pwd'}, "aa");
print {$file} "\n";
close $file;
}
sub enlistar_usuarios{
print qq|
<h1>Usuarios Activos</h1>
<form action="$cgiurl" method="post">
<table cellpadding="5" cellspacing="5">
<tr>
<td>
<select name="usuario" size="10">
|;
open my $file, '<', $htpasswd or die("No se pudo abrir .htpasswd: $!");
while(my $line = <$file>){
chomp($line);
my ($usuario, $pwd) = split(/\:/, $line);
print "<option value=\"$line\">$usuario</option>\n";
}
close $file;
print qq|
</td>
<td>
<input type="submit" name="eliminar_username" value=" Eliminar Usuario" >
</td>
</tr>
</table>
</form>
<hr>
|;
}
sub eliminar_usuario{
open my $in, '<', $htpasswd or die("No se pudo leer archivo .htpasswd: $!");
my @usuarios = <$in>;
close $in;
open my $out, '>', $htpasswd or die("No se pudo leer archivo .htpasswd: $!");
for my $usuario(@usuarios){
chomp($usuario);
if($usuario ne $Input{'usuario'}){
print {$out} $usuario, "\n";
}
}
close ($out);
}
Bibliografía
http://httpd.apache.org/docs/1.3/howto/htaccess.html - Apache Tutorial: .htaccess files
http://www.euronet.nl/~arnow/htpasswd/documentation.html - HTPASSWD Documentation