Cuenta de externas, arreglos de seguridad, títulos arriba y despliegue

- Rol «externas» para la asamblea de externas: pide sin cupo, siempre con el
  colectivo de fuera y su contacto, y sus solicitudes salen en naranja hasta
  que la asamblea de la Enre las confirma.
- /pedir/ explica en una línea que los colectivos externos piden fecha en la
  asamblea de externas, los lunes de 19:00 a 20:00.
- Seguridad: argon2 limitado a dos cálculos a la vez y el intento se cuenta
  antes de calcular (60 accesos simultáneos llevaban el proceso a 3,8 GB);
  bloqueo por cuenta a 20 fallos para que no se pueda dejar fuera a la
  asamblea; avisos por sesión en vez de por URL; sig sin «..»; eventos de
  31 días como mucho; parámetros de argon2 acotados.
- Títulos pegados arriba en todas las páginas; la franja de la enredadera
  recupera ese alto para seguir llenando la pantalla.
- despliegue/: instalar.sh, cuenta.sh, servicio de systemd con el proceso
  encerrado, fragmento de nginx que además deja de servir .git y las notas
  internas, y configuración de ejemplo. Ninguna contraseña en el repositorio.
This commit is contained in:
SITO 2026-09-23 12:15:33 +02:00
parent ce3127baff
commit 7d5dbeec60
24 changed files with 606 additions and 96 deletions

View file

@ -25,7 +25,8 @@ type App struct {
cfg Config
alm Almacen
ses *Sesiones
cer *Cerrojo
cer *Cerrojo // por IP
cerU *Cerrojo // por cuenta
auth *AuthLog
// mu serializa las escrituras de eventos: comprobar las reglas y guardar
@ -51,7 +52,7 @@ func main() {
local = flag.Bool("local", false, "sirve tambien la web estatica, solo para probar sin nginx")
usuario = flag.String("usuario", "", "usuario, para las ordenes cuenta y clave")
nombre = flag.String("nombre", "", "nombre visible del colectivo, para la orden cuenta")
rol = flag.String("rol", string(RolInterno), "rol de la cuenta: enre o interno")
rol = flag.String("rol", string(RolInterno), "rol de la cuenta: enre, interno o externas")
claveFija = flag.String("clave", "", "clave temporal para cuenta y clave; sin ella se genera una al azar")
definitiva = flag.Bool("definitiva", false, "con -clave: la clave no caduca al entrar (minimo 12 caracteres)")
)
@ -156,6 +157,7 @@ func NuevaApp(cfg Config) (*App, error) {
alm: reg,
ses: NuevasSesiones(inactividadSesion),
cer: NuevoCerrojo(maxIntentos, ventanaIntentos, esperaBloqueo),
cerU: NuevoCerrojo(maxIntentosCuenta, ventanaIntentos, esperaBloqueo),
auth: al,
plantillas: plantillas,
}, nil
@ -177,8 +179,17 @@ func (a *App) nombres() map[string]string {
return out
}
func (a *App) roles() map[string]Rol {
out := map[string]Rol{}
for _, c := range a.alm.Cuentas() {
out[c.Usuario] = c.Rol
}
return out
}
func (a *App) vista() Vista {
return Vista{
roles: a.roles(),
reglas: a.cfg.Reglas,
exc: a.alm.Excepciones(),
eventos: a.alm.Eventos(),
@ -224,8 +235,8 @@ func (a *App) AltaCuenta(usuario, nombre string, rol Rol, fija string, definitiv
if nombre == "" {
return errors.New("falta -nombre: el nombre visible del colectivo")
}
if rol != RolEnre && rol != RolInterno {
return fmt.Errorf("el rol solo puede ser %q o %q", RolEnre, RolInterno)
if !rol.Valido() {
return fmt.Errorf("el rol solo puede ser %q, %q o %q", RolEnre, RolInterno, RolExternas)
}
if _, existe := a.alm.BuscarCuenta(usuario); existe {
return fmt.Errorf("la cuenta %q ya existe; para cambiarle la clave usa la orden clave", usuario)