[cpif] r363 - in trunk: . backend frontend-web
svn at argo.es
svn at argo.es
Thu Sep 13 13:41:56 CEST 2007
Author: jcea
Date: Thu Sep 13 13:41:52 2007
New Revision: 363
Log:
El alta de usuarios es configurable:
solo "master", libre, o libre con
confirmación del "master".
Added:
trunk/frontend-web/url_new_user_confirmation.py
- copied, changed from r362, /trunk/frontend-web/url_nuevo_usuario_POST.py
Modified:
trunk/TODO
trunk/backend/database.py
trunk/backend/upgrade.py
trunk/frontend-web/skins.py
trunk/frontend-web/url_edit_config.py
trunk/frontend-web/url_nuevo_usuario.py
trunk/frontend-web/url_nuevo_usuario_POST.py
trunk/frontend-web/url_perfil.py
Modified: trunk/TODO
==============================================================================
--- trunk/TODO (original)
+++ trunk/TODO Thu Sep 13 13:41:52 2007
@@ -357,3 +357,24 @@
para poder meter la web como subseccion de otra. Ojo con
las redirecciones...
+- 20070913: FRONTAL WEB: Cuando un usuario se da de alta (si
+ se configura el servidor para permitirlo), el usuario
+ no existe realmente hasta que pulsa un enlace que le llega
+ por correo electronico. Hay que controlar que la estructura
+ que contiene las altas "pendientes" no crece de forma
+ ilimitada.
+
+- 20070913: FRONTAL WEB: Ahora que los usuarios pueden darse de
+ alta directamente, hay que tener mucho cuidado con los
+ ataques XSS, especialmente porque el administrador puede
+ listar esos perfiles.
+
+- 20070913: FRONTAL WEB: Si los usuarios pueden darse de alta,
+ pero el "master" debe confirmarla, el usuario recibe un
+ aviso de que se le notificara la decision por correo electronico...
+ pero no se envia nunca!!!.
+
+- 20070913: Poder dar de baja usuarios que no han hecho login
+ nunca y que no tienen ningun mensaje privado. El caso evidente
+ son nuevas altas.
+
Modified: trunk/backend/database.py
==============================================================================
--- trunk/backend/database.py (original)
+++ trunk/backend/database.py Thu Sep 13 13:41:52 2007
@@ -1,7 +1,7 @@
# $Id$
-VERSION_DB="2007090603"
+VERSION_DB="2007091303"
import globales
@@ -34,6 +34,7 @@
usuarios=PersistentDict()
usuarios["usuarios"]=BTree()
usuarios["num_usuarios"]=0
+ usuarios["pending_sign_ins"]=BTree()
root["usuarios"]=usuarios
metatags=PersistentDict()
@@ -74,6 +75,7 @@
conf["http_max_clients"]=16
conf["http_initial_timeout"]=30
conf["http_keep_alive_timeout"]=5
+ conf["user_sign_in_mode"]=0 # Only "master" can create new users
conf["openid_support"]=False # Because You could not have the libs!
conf["allow_bbcode"]=True
conf["allow_html"]=True
@@ -103,6 +105,13 @@
setattr(globales,k,v)
+def usuario_new_cookie(conn,nick) :
+ root=conn.get_root()
+ import random,sys
+ cookie=str(random.randint(0,sys.maxint))+str(random.randint(0,sys.maxint))
+ root["usuarios"]["usuarios"][normaliza_nick(nick)]["cookie"]=cookie
+ return cookie
+
# Si suministramos una cookie, de ahi se saca el
# usuario y se comprueba su identidad.
# Si pasamos nick/clave, de ahi comprobamos tambien su identidad
@@ -114,9 +123,8 @@
if clave or force_login :
usuario=root["usuarios"]["usuarios"].get(normaliza_nick(nick),None)
if usuario and ((clave==usuario["clave"]) or force_login) :
- import random,sys
- cookie=str(random.randint(0,sys.maxint))+str(random.randint(0,sys.maxint))
- usuario["cookie"]=cookie
+ if "no_login" in usuario : return None
+ cookie=usuario_new_cookie(conn,nick)
nick=usuario["nick"]
cookie="%s-%s" %(nick,cookie)
return nick,cookie
@@ -133,6 +141,17 @@
return None
+def set_usuario_no_login(conn,nick) :
+ root=conn.get_root()
+ usuario=root["usuarios"]["usuarios"][normaliza_nick(nick)]
+ usuario["no_login"]=True
+
+def unset_usuario_no_login(conn,nick) :
+ root=conn.get_root()
+ usuario=root["usuarios"]["usuarios"][normaliza_nick(nick)]
+ if "no_login" in usuario :
+ del usuario["no_login"]
+
def get_static(conn,section,name) :
root=conn.get_root()
section=root["static"].get(section)
@@ -272,6 +291,34 @@
usuario["email"]=email
return True
+def get_usuario_pending(conn,nonce) :
+ root=conn.get_root()
+ usuarios_pending=root["usuarios"]["pending_sign_ins"]
+ v=usuarios_pending.get(nonce,None)
+ if not v : return None
+ del usuarios_pending[nonce]
+ return v[2:] # We don't return the time
+
+# If we add new params, we must update "usuario_add".
+# Also, we would need to update the confirmation code (via URL).
+def usuario_add_pending(conn,nick,datos,clave=None,email="",OpenIDs=set()) :
+ root=conn.get_root()
+ usuarios=root["usuarios"]
+ usuarios_pending=usuarios["pending_sign_ins"]
+
+ nick_normalizado=normaliza_nick(nick)
+ assert nick_normalizado not in usuarios["usuarios"]
+
+ import time,random,sys
+ while True :
+ k=str(random.randint(0,sys.maxint))
+ if k not in usuarios_pending : break
+
+ usuarios_pending[k]=(time.time(),False,nick,datos,clave,email,OpenIDs)
+
+ return k
+
+# If we add new params, we must update "usuario_add_pending".
def usuario_add(conn,nick,datos,clave=None,email="",OpenIDs=set()) :
from durus.btree import BTree
from durus.persistent_dict import PersistentDict
Modified: trunk/backend/upgrade.py
==============================================================================
--- trunk/backend/upgrade.py (original)
+++ trunk/backend/upgrade.py Thu Sep 13 13:41:52 2007
@@ -311,3 +311,15 @@
root["static"]["icons"]=BTree()
conn.commit()
+ if root["version del foro"]=="2007090603" :
+ print "Actualizando la base de datos: 2007090603 -> 2007091302"
+ root["version del foro"]="2007091302"
+ root["config"]["user_sign_in_mode"]=0 # Only "master" can create new users
+ conn.commit()
+
+ if root["version del foro"]=="2007091302" :
+ print "Actualizando la base de datos: 2007091302 -> 2007091303"
+ root["version del foro"]="2007091303"
+ root["usuarios"]["pending_sign_ins"]=BTree()
+ conn.commit()
+
Modified: trunk/frontend-web/skins.py
==============================================================================
--- trunk/frontend-web/skins.py (original)
+++ trunk/frontend-web/skins.py Thu Sep 13 13:41:52 2007
@@ -48,18 +48,19 @@
}
urls = {
- '/': ['metatags'],
- '/indice': ['group_date', 'threads', 'threads'],
- '/hilo': ['thread_entries'],
- '/nuevo_usuario': ['new_user'],
- '/login_err': ['login_err'],
- '/LOGIN': ['login'],
- '/error': [],
- '/stop': [],
- '/nuevo_usuario_POST': [],
- '/perfil_POST': [],
- '/toggle_readonly': [],
- '/toggle_sticky': [],
+ '/': ['metatags'],
+ '/indice': ['group_date', 'threads', 'threads'],
+ '/hilo': ['thread_entries'],
+ '/nuevo_usuario': ['new_user'],
+ '/login_err': ['login_err'],
+ '/LOGIN': ['login'],
+ '/error': [],
+ '/stop': [],
+ '/nuevo_usuario_POST': [],
+ '/perfil_POST': [],
+ '/toggle_readonly': [],
+ '/toggle_sticky': [],
+ '/new_user_confirmation': [],
}
import os
Modified: trunk/frontend-web/url_edit_config.py
==============================================================================
--- trunk/frontend-web/url_edit_config.py (original)
+++ trunk/frontend-web/url_edit_config.py Thu Sep 13 13:41:52 2007
@@ -20,6 +20,11 @@
texto.append('<tr><td>Simultaneous HTTP connections</td><td><input type="text" name="http_max_clients" size="5" value="%d" /></td></tr>' %globales.http_max_clients)
texto.append('<tr><td>Specify initial HTTP timeout</td><td><input type="text" name="http_initial_timeout" size="5" value="%d" /></td></tr>' %globales.http_initial_timeout)
texto.append('<tr><td>Keep-alive timeout<br/>(if zero or False, no keep-alive support)</td><td><input type="text" name="http_keep_alive_timeout" size="5" value="%d" /></td></tr>' %globales.http_keep_alive_timeout)
+
+ texto.append('<tr><td>New Users Sign In</td><td><input type="radio" name="user_sign_in_mode" value="0" %s />Only "master" can create new users<br/>' %("CHECKED" if globales.user_sign_in_mode==0 else ""))
+ texto.append('<input type="radio" name="user_sign_in_mode" value="1" %s />Users can sign in, but "master" must approve it<br/>' %("CHECKED" if globales.user_sign_in_mode==1 else ""))
+ texto.append('<input type="radio" name="user_sign_in_mode" value="2" %s />Users can sign in freely</td></tr>' %("CHECKED" if globales.user_sign_in_mode==2 else ""))
+
texto.append('<tr><td>OpenID Support<br/>(Requires library. See docs)</td><td><input type="checkbox" name="openid_support" %s /></td></tr>' % ("CHECKED" if globales.openid_support else ""))
texto.append('<tr><td>Allow BBcode?</td><td><input type="checkbox" name="allow_bbcode" %s /></td></tr>' % ("CHECKED" if globales.allow_bbcode else ""))
texto.append('<tr><td>Allow HTML?</td><td><input type="checkbox" name="allow_html" %s /></td></tr>' % ("CHECKED" if globales.allow_html else ""))
Copied: trunk/frontend-web/url_new_user_confirmation.py (from r362, /trunk/frontend-web/url_nuevo_usuario_POST.py)
==============================================================================
--- /trunk/frontend-web/url_nuevo_usuario_POST.py (original)
+++ trunk/frontend-web/url_new_user_confirmation.py Thu Sep 13 13:41:52 2007
@@ -1,82 +1,59 @@
# $Id$
from globales import monitor
+from globales import user_sign_in_mode
@monitor
def gestiona_url(conn,handler,path,usuario) :
- if not usuario : # Acceso anonimo
- import url_LOGIN
- return url_LOGIN.gestiona_url(handler,path,usuario)
-
- if len(path)!=1 : return None
+ if len(path)!=2 : return None
+ nonce=path[1]
import skins
pagina = skins.Skin(path,usuario)
pagina.load_dict({"page_title": "cpif - Alta exitosa"})
- if usuario!="master" :
- pagina.load_url(["error"])
- pagina.load_dict({"generic_message": "<h1>Solo el usuario master puede crear nuevos usuarios</h1>"})
- return (pagina.web())
-
- import cgi
- ctype,pdict=cgi.parse_header(handler.headers.getheader('content-type'))
- cuerpo=cgi.FieldStorage(fp=handler.rfile,headers=handler.headers,environ={'REQUEST_METHOD':'POST'},keep_blank_values=1)
- nick=cuerpo.getfirst("usuario")
- clave1=cuerpo.getfirst("clave1")
- clave2=cuerpo.getfirst("clave2")
- email=cuerpo.getfirst("email")
- try :
- nick=nick.strip()
- clave1=clave1.strip()
- clave2=clave2.strip()
- email=email.strip()
- except :
- nick="" # Generate an error
-
- if not (nick and clave1 and clave2 and email) : # Comprobamos el caso de campos inexistentes
- pagina.load_url(["error"])
- pagina.load_dict({"generic_message": "<h1>No puedes dejar ningun campo no opcional en blanco</h1>"})
- return pagina.web()
-
- OpenIDs=set()
- for i in xrange(1,6) :
- OpenID=cuerpo.getfirst("OpenID%d" %i)
- if OpenID : # Comprobamos el caso de campos inexistentes
- OpenID=OpenID.strip()
- if OpenID :
- if '"' in OpenID : # XSS
- pagina.load_url(["error"])
- pagina.load_dict({"generic_message": "<h1>Caracteres extraños en los servidores OpenID</h1>"})
- return pagina.web()
- OpenIDs.add(OpenID)
-
- for i in ['"',"'",'<','>','&'] :
- if i in nick :
- pagina.load_url(["error"])
- pagina.load_dict({"generic_message": "<h1>Caracteres extraños en el nombre de usuario</h1>"})
- return pagina.web()
+ import database
+ v=database.get_usuario_pending(conn,nonce)
- if clave1!=clave2 :
+ if not v :
pagina.load_url(["error"])
- pagina.load_dict({"generic_message": "<h1>Las claves introducidas no coinciden</h1>"})
+ pagina.load_dict({"generic_message": "<h1>URL incorrecta. Si ya ha confirmado su alta, no necesita volver a hacerlo.</h1>"})
return (pagina.web())
- for i in ['"',"'",'<','>','&',' ','\t'] : # Deberiamos poner realmente una lista de los permitidos
- if i in email:
- error="<h1>Caracteres extraños en la direccion</h1>"
- pagina.load_url(["error"])
- pagina.load_dict({"generic_message": "<h1>Caracteres extraños en la dirección de correo</h1>"})
- return pagina.web()
+ nick,datos,clave,email,OpenIDs=v
- import database
if database.usuario_verifica(conn,nick=nick) :
pagina.load_url(["error"])
pagina.load_dict({"generic_message": "<h1>El usuario ya existe</h1>"})
return (pagina.web())
- database.usuario_add(conn,nick,None,clave=clave1,email=email,OpenIDs=OpenIDs)
+ database.usuario_add(conn,nick,None,clave=clave,email=email,OpenIDs=OpenIDs)
+ if user_sign_in_mode==1 :
+ database.set_usuario_no_login(conn,nick) # We need the master aproval
+
+ from globales import smtp_email_sender,base_url,admin_email_address
+ texto=["From: %s" %smtp_email_sender]
+ texto.append("To: %s" %admin_email_address)
+ texto.append("Subject: Confirmacion de alta de usuario")
+ texto.append("")
+ texto.append("Un usuario ha confirmado el alta, pero se requiere")
+ texto.append("tambien la confirmacion del administrador.")
+ texto.append("")
+ texto.append("Como administrador, puede ver el perfil del usuario")
+ texto.append("pulsando sobre el siguiente enlace:")
+ texto.append("")
+ texto.append("%s/perfil/%s" %(base_url,nick))
+
+ import smtp
+ smtp.enqueue(conn,[admin_email_address],"\r\n".join(texto))
+
+ pagina.load_dict({"generic_message": "<h1>Alta exitosa, pero se requiere la confirmacion del administrador. Recibirá un mensaje de correo electrónico con la decisión.</h1>"})
+ return (pagina.web())
- pagina.load_dict({"generic_message": "<h1>Alta exitosa</h1>"})
- return (pagina.web())
+ resultado=database.usuario_verifica(conn,nick=nick,force_login=True)
+ assert resultado
+ nick,cookie=resultado
+ if cookie :
+ cookie="cpif_auth=%s; path=/; expires=Wed, 01-Jan-2030 00:00:00 GMT;" %cookie
+ return (302,{"Set-Cookie":cookie,"Location":"/"},"")
Modified: trunk/frontend-web/url_nuevo_usuario.py
==============================================================================
--- trunk/frontend-web/url_nuevo_usuario.py (original)
+++ trunk/frontend-web/url_nuevo_usuario.py Thu Sep 13 13:41:52 2007
@@ -1,6 +1,7 @@
# $Id$
from globales import monitor
+from globales import user_sign_in_mode
def gestiona_url(handler,path,usuario) :
if len(path)!=1 : return None
@@ -8,7 +9,7 @@
pagina = skins.Skin(path,usuario)
pagina.load_dict({"page_title": "cpif - Añadir nuevo usuario"})
- if usuario!="master" :
+ if (usuario!="master") and (user_sign_in_mode==0) :
pagina.load_url(["error"])
pagina.load_dict({"generic_message": "<h1>Solo el usuario 'master' puede dar de alta nuevos usuarios</h1>"})
Modified: trunk/frontend-web/url_nuevo_usuario_POST.py
==============================================================================
--- trunk/frontend-web/url_nuevo_usuario_POST.py (original)
+++ trunk/frontend-web/url_nuevo_usuario_POST.py Thu Sep 13 13:41:52 2007
@@ -1,20 +1,17 @@
# $Id$
from globales import monitor
+from globales import user_sign_in_mode
@monitor
def gestiona_url(conn,handler,path,usuario) :
- if not usuario : # Acceso anonimo
- import url_LOGIN
- return url_LOGIN.gestiona_url(handler,path,usuario)
-
if len(path)!=1 : return None
import skins
pagina = skins.Skin(path,usuario)
pagina.load_dict({"page_title": "cpif - Alta exitosa"})
- if usuario!="master" :
+ if (usuario!="master") and (user_sign_in_mode==0) :
pagina.load_url(["error"])
pagina.load_dict({"generic_message": "<h1>Solo el usuario master puede crear nuevos usuarios</h1>"})
return (pagina.web())
@@ -75,8 +72,26 @@
pagina.load_dict({"generic_message": "<h1>El usuario ya existe</h1>"})
return (pagina.web())
- database.usuario_add(conn,nick,None,clave=clave1,email=email,OpenIDs=OpenIDs)
+ if usuario=="master" :
+ database.usuario_add(conn,nick,None,clave=clave1,email=email,OpenIDs=OpenIDs)
+ pagina.load_dict({"generic_message": "<h1>Alta exitosa</h1>"})
+ else :
+ nonce=database.usuario_add_pending(conn,nick,None,clave=clave1,email=email,OpenIDs=OpenIDs)
+
+ from globales import smtp_email_sender,base_url
+ texto=["From: %s" %smtp_email_sender]
+ texto.append("To: %s" %email)
+ texto.append("Subject: Confirmacion de alta de usuario")
+ texto.append("")
+ texto.append("Para confirmar tu usuario, pulsa sobre el")
+ texto.append("siguiente enlace:")
+ texto.append("")
+ texto.append("%s/new_user_confirmation/%s" %(base_url,nonce))
+
+ import smtp
+ smtp.enqueue(conn,[email],"\r\n".join(texto))
+
+ pagina.load_dict({"generic_message": "<h1>Se le ha enviado un mensaje de correo electrónico. Siga sus instrucciones.</h1>"})
- pagina.load_dict({"generic_message": "<h1>Alta exitosa</h1>"})
return (pagina.web())
Modified: trunk/frontend-web/url_perfil.py
==============================================================================
--- trunk/frontend-web/url_perfil.py (original)
+++ trunk/frontend-web/url_perfil.py Thu Sep 13 13:41:52 2007
@@ -26,7 +26,7 @@
if not u :
return None
- texto=["<tr><td>Usuario:</td><td>%s</td></tr>" %usuario_pedido]
+ texto=["<tr><td>Usuario:</td><td>%s%s</td></tr>" %(usuario_pedido, " <b><i>¡¡NO LOGIN!!</i></b>" if "no_login" in u else "")]
texto.append('<tr><td>Clave nueva:</td><td><input type="password" name="clave1" size="25" value="" /></td></tr>')
texto.append('<tr><td>Confirma clave nueva:</td><td><input type="password" name="clave2" size="25" value="" /></td></tr>')
a="(Se ha solicitado un cambio de dirección de correo electrónico, y está pendiente de confirmación)" if "new email" in u else ""
More information about the cpif
mailing list