# 9.2.3 Creando las tablas para manejar la seguridad

#### Creando nuestra tabla Usuarios

Siguiendo las recomendaciones de seguridad de la lección anterior voy a crear la tabla usuarios de la siguiente manera:

{% code title="Usuario.cs" %}

```csharp
public class Usuario
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(15, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(15)")]
    public string Clave { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(255, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(255)")]
    public string Password { get; set; }

    [Required(ErrorMessage = "Required")]
    public bool Activo { get; set; }

    [Required(ErrorMessage = "Required")]
    public string Adicional1 { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(200, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(200)")]
    public string Nombre { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(80, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(80)")]
    public string Email { get; set; }
}
```

{% endcode %}

Agregamos un indice único para la clave del usuario

{% code title="UsuarioConfiguration.cs" %}

```csharp
public class : IEntityTypeConfiguration<Usuario>
{
    public void Configure(EntityTypeBuilder<Usuario> builder)
    {
         builder.HasIndex(e => e.Clave)
                .HasName("UI_UsuarioClave")
                .IsUnique();
    }
}
```

{% endcode %}

#### Creando nuestra tabla UsuarioAcceso

Agregamos nuestra tabla UsuarioAcceso para registrar los accesos de nuestros usuarios, como el navegador, ciudad, estado, país, sistema operativo desde donde inicia sesión el usuario

{% code title="UsuarioAcceso.cs" %}

```csharp
public class UsuarioAcceso
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Required")]
    public int UsuarioId { get; set; }

    [Required(ErrorMessage = "Required")]
    public DateTime Fecha { get; set; }

    [Column(TypeName = "VARCHAR(300)")]
    [Required(ErrorMessage = "Required")]
    public string Token { get; set; }

    [Required(ErrorMessage = "Required")]
    public bool Activo { get; set; }

    [Required(ErrorMessage = "Required")]
    [Column(TypeName = "VARCHAR(200)")]
    public string SistemaOperativo { get; set; }

    [Required(ErrorMessage = "Required")]
    [Column(TypeName = "VARCHAR(200)")]
    public string Navegador { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(300, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(300)")]
    public string Ciudad { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(300, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(300)")]
    public string Estado { get; set; }

    [Required(ErrorMessage = "Required")]
    [Column(TypeName = "VARCHAR(200)")]
    public string RefreshToken { get; set; }

    [Required(ErrorMessage = "Required")]
    public DateTime FechaRefresh { get; set; }

    [Required(ErrorMessage = "Required")]
    public bool MantenerSesion { get; set; }
    
}
```

{% endcode %}

Creamos nuestras llaves foráneas e índices. Vamos a agregar un índice para el campo refresh token, y otro para el usuario y el token

{% code title="UsuarioAccesoConfiguration.cs" %}

```csharp
public class UsuarioAccesoConfiguration 
                         : IEntityTypeConfiguration<UsuarioAcceso>
{
    public void Configure(EntityTypeBuilder<UsuarioAcceso> builder)
    {
        builder.HasOne(typeof(Usuario))
               .WithMany()
               .OnDelete(DeleteBehavior.Restrict);
                   
        builder.HasIndex(u => u.RefreshToken)
               .HasName("UI_RefreshToken")
               .IsUnique();

        builder.HasIndex(u => new { u.UsuarioId, u.Token })
               .HasName("UI_Token")
               .IsUnique();
    }
}
```

{% endcode %}

#### Creando nuestra tabla UsuarioRol

En esta tabla vamos a guardar todos los roles que tiene un usuario. Esto permite a un mismo usuario poder accesar como vendedor, administrador o cliente.

{% code title="UsuarioRol.cs" %}

```csharp
public class UsuarioRol
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Required")]
    public int UsuarioId { get; set; }

    [Required(ErrorMessage = "Required")]
    public int RolId { get; set; }
}
```

{% endcode %}

Agregamos nuestra llaves foráneas e índices

{% code title="UsuarioRolConfiguration.cs" %}

```csharp
public class UsuarioRolConfiguration 
                                 : IEntityTypeConfiguration<UsuarioRol>
{
    public void Configure(EntityTypeBuilder<UsuarioRol> builder)
    {
        builder.HasIndex(u => new { u.UsuarioId, u.RolId })
               .HasName("UI_UsuarioRol")
               .IsUnique();

        builder.HasOne(typeof(Usuario))
               .WithMany()
               .OnDelete(DeleteBehavior.Restrict);

        builder.HasOne(typeof(Rol))
               .WithMany()
               .OnDelete(DeleteBehavior.Restrict);
    }
}
```

{% endcode %}

#### Agregando la tabla UsuarioCliente

Esta tabla nos permitirá registrar los usuarios que tiene cada cliente, de esta manera un usuario no puede ver los productos de otro cliente.

{% code title="UsuarioCliente.cs" %}

```csharp
public class UsuarioCliente
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Required")]
    public int UsuarioId { get; set; }

    [Required(ErrorMessage = "Required")]
    public int ClienteId { get; set; }
}
```

{% endcode %}

Agregamos nuestras llaves foráneas e índices

{% code title="UsuarioClienteConfiguration.cs" %}

```csharp
public class UsuarioClienteConfiguration 
                         : IEntityTypeConfiguration<UsuarioCliente>
{
    public void Configure(EntityTypeBuilder<UsuarioCliente> builder)
    {
        builder.HasIndex(u => new { u.UsuarioId, u.ClienteId })
           .HasName("UI_UsuarioCliente")
           .IsUnique();

        builder.HasOne(typeof(Usuario))
               .WithMany()
               .OnDelete(DeleteBehavior.Restrict);

        builder.HasOne(typeof(Cliente))
               .WithMany()
               .OnDelete(DeleteBehavior.Restrict);
    }
}
```

{% endcode %}

#### Agregando nuestra tabla Tabla

Para cada tabla registrada en el sistema, vamos a guardar un historial de cambios realizados a la tabla, en el cual vamos a guardar la fecha y hora en que se agrega, borra o modifica un registro, el usuario que realizó el cambio al registro, observaciones adicionales sobre el cambio realizado, y la tabla en que se realiza el cambio, para esto voy a registrar en una tabla llamada **tabla** los datos de la tabla que se esta modificando

{% code title="Tabla.cs" %}

```csharp
public class Tabla
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(40, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(40)")]
    public string Nombre { get; set; }

    [Required(ErrorMessage = "Required")]
    [StringLength(40, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(200)")]
    public string Descripción { get; set; }
}
```

{% endcode %}

#### Agregando nuestra tabla historial

```csharp
public class Historial
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Required")]
    public int TablaId { get; set; }

    [Required(ErrorMessage = "Required")]
    public int OrigenId { get; set; }

    [Required(ErrorMessage = "Required")]
    public int Actividad { get; set; }

    [Required(ErrorMessage = "Required")]
    [ForeignKey("Usuario")]
    public int? UsuarioId { get; set; }

    [Required(ErrorMessage = "Required")]
    public DateTime FechaHora { get; set; }

    [StringLength(250, ErrorMessage = "StringLength")]
    [Column(TypeName = "VARCHAR(250)")]
    public string Observa { get; set; }
}
```

Agregamos nuestras llaves foráneas e índices

{% code title="HistorialConfiguration.cs" %}

```csharp
public class HistorialConfiguration : IEntityTypeConfiguration<Historial>
{
    public void Configure(EntityTypeBuilder<Historial> builder)
    {
        builder.HasIndex(h => h.TablaId)
               .HasName("IX_HistorialTabla");

        builder.HasIndex(e => e.UsuarioId)
               .HasName("IX_ctrUsuario");

        builder.HasIndex(e => new { e.Actividad, e.TablaId, e.FechaHora })
               .HasName("IX_Actividad");

        builder.HasIndex(e => new { e.TabladId, e.OrigenId, e.Actividad })
               .HasName("IX_Historial");

        builder.HasOne(typeof(Tabla))
               .WithMany()
               .OnDelete(DeleteBehavior.Restrict);

        builder.HasOne(typeof(Usuario))
            .WithMany()
            .OnDelete(DeleteBehavior.Restrict);
    }
}
```

{% endcode %}

Agregamos nuestras tablas y archivos de configuración a nuestro archivo **CaducaContext**

{% code title="CaducaContext.cs" %}

```csharp
public class CaducaContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new HistorialConfiguration());
        modelBuilder.ApplyConfiguration(new UsuarioAccesoConfiguration());
        modelBuilder.ApplyConfiguration(new UsuarioClienteConfiguration());
        modelBuilder.ApplyConfiguration(new UsuarioConfiguration());
        modelBuilder.ApplyConfiguration(new UsuarioRolConfiguration());        
        
        public virtual DbSet<Historial> Historial { get; set; }
        public virtual DbSet<Tabla> Tabla { get; set; }
        public virtual DbSet<Usuario> Usuario { get; set; }
        public virtual DbSet<UsuarioAcceso> UsuarioAcceso { get; set; }
        public virtual DbSet<UsuarioCliente> UsuarioCliente { get; set; }
        public virtual DbSet<UsuarioRol> UsuarioRol { get; set; }
    } 
}
```

{% endcode %}

Agregamos nuestra migración

```
Add-Migration TablasSeguridad
```

Actualizamos nuestra base de datos

```
Update-Database
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://abi.gitbook.io/net-core/7.-seguridad/7.2-seguridad-basada-en-roles/7.5-creando-tu-tabla-usuarios.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
