README.md

# AuthManager

Una biblioteca completa para gestión de usuarios, roles y permisos en aplicaciones Elixir y Phoenix.

## Características

- Sistema de usuarios con datos personales
- Jerarquía de roles y permisos con herencia
- Autenticación basada en JWT con Guardian
- Middleware para verificación de permisos
- Herramientas utilitarias para strings, mapas, listas, fechas y criptografía
- Fácil integración con aplicaciones Phoenix
- Soporte para Phoenix Router y Controllers
- Rutas de autenticación predefinidas

## Instalación

Agrega `auth_manager` a tus dependencias en `mix.exs`:

```elixir
def deps do
  [
    {:auth_manager, "~> 0.1.0"}
  ]
end
```

## Configuración

### Configuración Básica

Configura tu repositorio Ecto en tu archivo `config/config.exs`:

```elixir
config :auth_manager,
  repo: MyApp.Repo,
  endpoint: MyAppWeb.Endpoint
```

### Configuración de Guardian

Para la autenticación con JWT, configura Guardian:

```elixir
config :auth_manager, AuthManager.Accounts.Guardian,
  issuer: "my_app",
  secret_key: "tu_clave_secreta", # O mejor usar System.get_env("SECRET_KEY_BASE")
  ttl: {60, :minute}
```

### Migraciones

Ejecuta las migraciones:

```bash
mix ecto.gen.migration copy_auth_manager_migrations
```

En el archivo de migración generado, copia las migraciones de AuthManager:

```elixir
defmodule MyApp.Repo.Migrations.CopyAuthManagerMigrations do
  use Ecto.Migration

  def up do
    AuthManager.Migrations.CreateUsers.up()
    AuthManager.Migrations.CreateRoles.up()
    AuthManager.Migrations.CreatePermissions.up()
    AuthManager.Migrations.CreateUserRoles.up()
    AuthManager.Migrations.CreateUserPermissions.up()
    AuthManager.Migrations.CreateRolePermissions.up()
    AuthManager.Migrations.CreateRoleRoles.up()
  end

  def down do
    AuthManager.Migrations.CreateRoleRoles.down()
    AuthManager.Migrations.CreateRolePermissions.down()
    AuthManager.Migrations.CreateUserPermissions.down()
    AuthManager.Migrations.CreateUserRoles.down()
    AuthManager.Migrations.CreatePermissions.down()
    AuthManager.Migrations.CreateRoles.down()
    AuthManager.Migrations.CreateUsers.down()
  end
end
```

Luego ejecuta:

```bash
mix ecto.migrate
```

### Datos iniciales

Para crear los roles y permisos básicos:

```elixir
# En tu archivo de semillas (seeds.exs)
AuthManager.Core.Seeds.ensure_seeds_exist()
```

## Uso básico

### Crear y gestionar usuarios

```elixir
# Crear un nuevo usuario
{:ok, user} = AuthManager.create_user(%{
  first_name: "Juan",
  last_name: "Pérez",
  username: "juanperez",
  email: "juan@example.com",
  password: "secretpassword"
})

# Crear un rol
{:ok, admin_role} = AuthManager.create_role(%{
  name: "Administrador",
  description: "Acceso completo al sistema"
})

# Crear un permiso
{:ok, manage_users} = AuthManager.create_permission(%{
  name: "Gestionar usuarios",
  slug: "manage_users",
  description: "Permite gestionar usuarios del sistema"
})

# Asignar un rol a un usuario
{:ok, _} = AuthManager.assign_role_to_user(user, admin_role)

# Asignar un permiso a un rol
{:ok, _} = AuthManager.assign_permission_to_role(admin_role, manage_users)

# Verificar si un usuario tiene un permiso
AuthManager.can?(user, "manage_users")  # true

# Verificar si un usuario tiene un rol
AuthManager.has_role?(user, "administrador")  # true

# Función can_by? flexible
AuthManager.can_by?(user: user, permission: "manage_users")  # true
AuthManager.can_by?(user: user, role: "administrador")  # true
AuthManager.can_by?(role: admin_role, permission: "manage_users")  # true
```

### Autenticación de usuarios

```elixir
alias AuthManager.Accounts.UserService
alias AuthManager.Accounts.Guardian

# Autenticar un usuario
case UserService.authenticate("username", "password") do
  {:ok, user} ->
    # Generar tokens JWT
    access_token = Guardian.create_access_token(user)
    refresh_token = Guardian.create_refresh_token(user)
    # ...
    
  {:error, :invalid_credentials} ->
    # Manejar error de autenticación
    # ...
end

# Verificar un token y obtener el usuario
case Guardian.get_user_from_token(token) do
  {:ok, user} ->
    # Usuario autenticado
    # ...
    
  {:error, _reason} ->
    # Token inválido
    # ...
end

# Refrescar tokens
case Guardian.exchange_refresh_token(refresh_token) do
  {:ok, new_access_token, new_refresh_token} ->
    # ...
    
  {:error, _reason} ->
    # ...
end
```

### Integración con Phoenix

#### Configuración del Router

```elixir
defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  use AuthManager.Phoenix, :router  # Añade macros para autenticación
  
  # Añadir pipeline de autenticación
  auth_pipeline()
  
  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    # ...
  end
  
  pipeline :api do
    plug :accepts, ["json"]
  end
  
  pipeline :api_auth do
    plug AuthManager.Accounts.AuthenticationPlug
  end
  
  # Rutas protegidas para la API
  scope "/api", MyAppWeb do
    pipe_through [:api, :api_auth]
    
    resources "/users", UserController
  end
  
  # Añadir rutas de autenticación estándar (opcional)
  # auth_routes()
  
  # Añadir rutas de API de autenticación (opcional)
  # auth_api_routes()
end
```

#### Uso en Controllers

```elixir
defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller
  use AuthManager.Phoenix, :controller  # Añade plugs y helpers para autenticación
  import AuthManager.Core.Middleware
  
  # Verificar autenticación del usuario
  plug :require_authenticated_user when action in [:index, :show, :edit, :update, :delete]
  
  # Verificar un permiso específico
  plug require_permission(permission: "manage_users") when action in [:index, :show, :edit, :update, :delete]
  
  # O usar authorize para verificaciones más complejas
  plug authorize(role: "admin") when action in [:dangerous_action]
  
  def index(conn, _params) do
    users = AuthManager.get_all_users()
    render(conn, "index.html", users: users)
  end
  
  # ...
end
```

### Herencia de roles y permisos

```elixir
# Crear roles con jerarquía
{:ok, editor_role} = AuthManager.create_role(%{
  name: "Editor",
  description: "Puede editar contenido"
})

{:ok, senior_editor_role} = AuthManager.create_role(%{
  name: "Editor Senior",
  description: "Editor con permisos adicionales"
})

# Establecer jerarquía (senior_editor hereda de editor)
{:ok, _} = AuthManager.assign_parent_role(senior_editor_role, editor_role)

# Crear permisos con jerarquía
{:ok, edit_content} = AuthManager.create_permission(%{
  name: "Editar contenido",
  slug: "edit_content"
})

{:ok, publish_content} = AuthManager.create_permission(%{
  name: "Publicar contenido",
  slug: "publish_content"
})

# Asignar permiso al rol padre
{:ok, _} = AuthManager.assign_permission_to_role(editor_role, edit_content)

# Asignar permiso al rol hijo
{:ok, _} = AuthManager.assign_permission_to_role(senior_editor_role, publish_content)

# Un usuario con rol senior_editor tendrá ambos permisos
{:ok, user} = AuthManager.create_user(%{username: "senior_editor", email: "se@example.com", password: "password", first_name: "Senior", last_name: "Editor"})
{:ok, _} = AuthManager.assign_role_to_user(user, senior_editor_role)

# Verificar permisos
AuthManager.can?(user, "edit_content")     # true (heredado del rol editor)
AuthManager.can?(user, "publish_content")  # true (asignado directamente al rol senior_editor)
```

## Herramientas utilitarias

AuthManager incluye varios módulos de herramientas que pueden usarse independientemente:

### StringTools

```elixir
alias AuthManager.Tools.StringTools

StringTools.capitalize_words("hello world")  # "Hello World"
StringTools.to_snake_case("HelloWorld")      # "hello_world"
StringTools.slugify("¡Hola, Mundo!")         # "hola-mundo"
StringTools.to_camel_case("hello_world")     # "helloWorld"
StringTools.to_pascal_case("hello_world")    # "HelloWorld"
StringTools.to_kebab_case("HelloWorld")      # "hello-world"
StringTools.truncate("Hello World", 5)       # "Hello..."
StringTools.to_bytecode("ABC")               # [65, 66, 67]
StringTools.from_bytecode([65, 66, 67])      # "ABC"
StringTools.format_with_pattern("1234567890", "XXX-XXX-XXXX")  # "123-456-7890"
```

### MapTools

```elixir
alias AuthManager.Tools.MapTools

MapTools.keys_to_atoms(%{"name" => "Juan"})       # %{name: "Juan"}
MapTools.keys_to_strings(%{name: "Juan"})         # %{"name" => "Juan"}
MapTools.flatten(%{user: %{name: "Juan"}})        # %{"user.name" => "Juan"}
MapTools.unflatten(%{"user.name" => "Juan"})      # %{"user" => %{"name" => "Juan"}}
MapTools.deep_merge(%{a: 1}, %{b: 2})             # %{a: 1, b: 2}
MapTools.deep_filter(%{a: 1, b: nil}, fn {_,v} -> !is_nil(v) end)  # %{a: 1}
MapTools.deep_map(%{a: 1, b: 2}, fn v -> v * 2 end)  # %{a: 2, b: 4}
MapTools.get_in_path(%{user: %{name: "Juan"}}, "user.name")  # "Juan"
```

### ListTools

```elixir
alias AuthManager.Tools.ListTools

ListTools.flatten([1, [2, 3], [4, [5, 6]]])  # [1, 2, 3, 4, 5, 6]
ListTools.unique([1, 2, 3, 2, 1, 4])         # [1, 2, 3, 4]
ListTools.chunk([1, 2, 3, 4, 5, 6, 7], 3)    # [[1, 2, 3], [4, 5, 6], [7]]
ListTools.intersperse([1, 2, 3], :sep)       # [1, :sep, 2, :sep, 3]
ListTools.compress([1, 1, 2, 3, 3, 3, 4])    # [1, 2, 3, 4]
ListTools.rotate([1, 2, 3, 4, 5], 2)         # [3, 4, 5, 1, 2]
ListTools.sliding_window([1, 2, 3, 4, 5], 3) # [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
ListTools.permutations([1, 2, 3])            # [[1, 2, 3], [1, 3, 2], ...]
ListTools.combinations([1, 2, 3], 2)         # [[1, 2], [1, 3], [2, 3]]
```

### DateTools

```elixir
alias AuthManager.Tools.DateTools

DateTools.age_from_birthdate(~D[1990-05-15])  # Calcula la edad
DateTools.format(~D[2023-05-15], "{D} de {Mfull} de {YYYY}")  # "15 de mayo de 2023"
DateTools.add(~D[2023-05-15], days: 5)        # ~D[2023-05-20]
DateTools.add(~D[2023-05-15], months: 2)      # ~D[2023-07-15]
DateTools.start_of(:month, ~D[2023-05-15])    # ~D[2023-05-01]
DateTools.end_of(:month, ~D[2023-05-15])      # ~D[2023-05-31]
DateTools.between?(~D[2023-05-15], ~D[2023-05-10], ~D[2023-05-20])  # true
DateTools.date_range(~D[2023-05-15], ~D[2023-05-20])  # [~D[2023-05-15], ~D[2023-05-16], ...]
```

### CryptoTools

```elixir
alias AuthManager.Tools.CryptoTools

hash = CryptoTools.hash_password("secretpassword")
CryptoTools.verify_password("secretpassword", hash)  # true

token = CryptoTools.generate_token(16)  # Genera un token aleatorio
CryptoTools.sha256("hello world")       # "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
CryptoTools.hmac("datos", "secreto")    # Genera un HMAC SHA-256

# Cifrado AES-GCM
key = :crypto.strong_rand_bytes(32)
{ciphertext, iv, tag} = CryptoTools.encrypt("datos secretos", key)
CryptoTools.decrypt(ciphertext, iv, tag, key)  # "datos secretos"

# Más simple con codificación Base64
encrypted = CryptoTools.encrypt_and_encode("datos secretos", key)
CryptoTools.decode_and_decrypt(encrypted, key)  # "datos secretos"
```

## Personalización Avanzada

### Implementar Vistas personalizadas

Puede crear sus propias vistas y controladores que extiendan la funcionalidad básica:

```elixir
defmodule MyAppWeb.AuthView do
  use MyAppWeb, :view
  use AuthManager.Phoenix, :view
  
  # Ahora tienes disponible helpers como logged_in?(conn) y current_user(conn)
  # Puedes añadir funciones personalizadas
  def user_name(user) do
    "#{user.first_name} #{user.last_name}"
  end
end
```

### Extender la API

Puede extender la API creando sus propios módulos que utilicen AuthManager:

```elixir
defmodule MyApp.Auth do
  def create_role_with_permissions(role_attrs, permission_slugs) do
    # Transacción que crea un rol y le asigna permisos
    Ecto.Multi.new()
    |> Ecto.Multi.run(:role, fn _, _ -> 
      AuthManager.create_role(role_attrs)
    end)
    |> Ecto.Multi.run(:permissions, fn _, %{role: role} ->
      results = for slug <- permission_slugs do
        permission = AuthManager.Core.Controller.repo().get_by!(AuthManager.Authorization.Permission, slug: slug)
        AuthManager.assign_permission_to_role(role, permission)
      end
      
      {:ok, results}
    end)
    |> AuthManager.Core.Controller.repo().transaction()
  end
end
```

## Licencia

MIT