---
# System prepended metadata

title: Desafío 2 - Mecanismos de autenticación y control de accesos en una aplicación web
tags: [' evaluado', Desarrollo de aplicaciones con Ruby on Rails]

---

---
title: 'Desafío 2 - Mecanismos de autenticación y control de accesos en una aplicación web'
tags: Desarrollo de aplicaciones con Ruby on Rails, evaluado
---
{%hackmd hackmd-dark-theme %}

## Requerimiento 1: Set-up inicial del proyecto sus relaciones y modelos

Creación del proyecto:

```bash
rails new the_rial_news -d postgresql
```

Modificar datos de conexión a través de credenciales:

**bash**
```bash
EDITOR="code --wait" rails credentials:edit
```

**CMD**

```bash
set EDITOR=code --wait && rails credentials:edit
```

```yml=
db:
  username: user_db
  password: password_db
```

En el archivo database.yml: 

```yml=17
default: &default
  adapter: postgresql
  encoding: unicode
username: <%= Rails.application.credentials.db[:username] %>
password: <%= Rails.application.credentials.db[:password] %>
```

Crear la base de datos:
```bash
rails db:create
```

Entidades y relaciones 


Visto desde la perspectiva de comentarios
```mermaid
%%{
  init: {
    'theme': 'base',
    'themeVariables': {
	  'background': '#fff',
      'primaryColor': '#FFB439',
      'primaryTextColor': '#222',
      'primaryBorderColor': '#FFB439',
      'tertiaryColor': '#fff'
	 },
	 'themeCSS': '.er.attributeBoxEven { fill:#ddd} .er.relationshipLine { stroke:#ccc}'
  }
}%%
erDiagram
  COMMENT ||--|{ USER :escribe
  COMMENT ||--|{ PUBLICATION :esta
```

Visto desde la perspectiva de noticias y usuarios

```mermaid
%%{
  init: {
    'theme': 'base',
    'themeVariables': {
	  'background': '#fff',
      'primaryColor': '#FFB439',
      'primaryTextColor': '#222',
      'primaryBorderColor': '#FFB439',
      'tertiaryColor': '#fff'
	 },
	 'themeCSS': '.er.attributeBoxEven { fill:#ddd} .er.relationshipLine { stroke:#ccc}'
  }
}%%
erDiagram
  USER ||--|{ COMMENT :escribe
  ADMIN ||--|{ PUBLICATION :publica
  COMMENT ||--|{ PUBLICATION :esta
```


Añadir Devise al proyecto:

```bash
bundle add devise
```

Instalar el generador de Devise:

```bash
rails g devise:install
```

Establecer las noticias como el root path:

```ruby
Rails.application.routes.draw do
  devise_for :users
  root "publications#index"
end
```

Crear el modelo de usuario con el generador de Devise:

```bash
rails g devise User name
```

Generar un scaffold para el modelo Publiciones (*noticias*):

```bash
rails g scaffold Publication user:references title description:text 
```

Generar un modelo para comentarios:

```bash
rails g model Comment users:references publications:references content:text
```

Generamos los controladores para comentarios:

```bash
rails g controller Comments
```

Arreglamos las relaciones

```ruby=
class Publication < ApplicationRecord
  belongs_to :user
  has_many :comments
end
```
```ruby
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  enum :role, [:normal_user, :admin]
  has_many :publications
  has_many :comments
end
```

Y también arreglamos las rutas para anidarla en las noticias: 

```ruby
Rails.application.routes.draw do
  devise_for :users
  resource :puublications do
    resource :comments, only: [:create]
  end
  root "publications#index"
end
```


Ejecutar la migración:

```bash
rails db:migrate
```

Generar las vistas de devise:

```bash
rails g devise:views
```


---

## Requerimiento 2 


*el usuario que no se haya registrado o iniciado sesión solo podrá ver las publicaciones e ingresar a su detalle.  
(Los usuarios registrados podrán dejar comentarios en las noticias, que podrán ser eliminados por parte de los administradores si no lo consideran apropiados)*

```ruby
before_action :authenticate_user!, except: %i[ index show ]
```

Agregar roles a los usuarios con sus permisos.


```bash
rails g migration addRoleToUsers role:integer
```

Se genera la siguiente migración, le añadimos un default para que al momento de crear un usuario, tenga el mínimo de permisos: 

```ruby=
class AddRoleToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :role, :integer
  end
end
```

Ejecutamos la migración:

```bash
rails db:migrate
```

Por consecuencia, los usuarios que ya existian se le asignara el role=0:  


```
-[ RECORD 1 ]----------+-------------------------------
id                     | 2
email                  | existente911@gmail.com
encrypted_password     | $2a$12$iBONMTcrOsgjCFVybYZR... 
reset_password_token   | 4d017a6df9e1641ba2852609cd6...
reset_password_sent_at | 2023-06-09 21:44:57.64205
remember_created_at    |
created_at             | 2023-06-09 19:05:49.842948
updated_at             | 2023-06-09 21:44:57.64205     
role                   | 0
```

Usando el método **enum** creamos los roles:

```ruby
enum :role, [:normal_user, :admin]
```

Creamos el método para verificar el tipo de usuario:

```ruby
class ApplicationController < ActionController::Base
    def authorized_request(kind = nil)
        unless kind.include?(current_user.role)
            redirect_to publications_path, notice: "Solo el administrador puede realizar esta acción"
        end
    end
end
```

Y ahora vamos al controlador de noticias (*publicaciones*) para utilizar este método: 

```ruby
before_action only: %i[ new create edit update destroy ] do
    authorize_request(["admin"])
end
```

## Requerimiento 3

Si un usuario olvida su contraseña, puede recibir un email para restablecerla.


**Configurar GMAIL para devise:** 


{%speakerdeck mach911/contrasena-para-aplicaciones-gmail %}

agregamos lo siguiente en el archivo `config/environments/development.rb`:

```ruby=40
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.perform_caching = false
config.action_mailer.delivery_method = :sendmail
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
    address: 'smtp.gmail.com',
    port: 587,
    domain: 'example.com',
    user_name: '[Gmail Acc]',
    password: '[Gmail password]',
    authentication: 'plain',
    enable_starttls_auto: true
  }
```


## Requerimiento 4

Las noticias (publicaciones) deben tener sus tests funcionales.

**Setup**:

```ruby
include Devise::Test::IntegrationHelpers

setup do
    @publication = publications(:one)
    @user = users(:one)
end
```

Test para la acción **index** de noticias:

```bash
rails t test/controllers/publications_controller_test.rb -n test_should_get_index
```

```ruby
test "should get index" do
    get publications_url
    assert_response :success
    assert_equal 200, response.status
    assert_equal "utf-8", response.charset
end
```

Test para la acción **new** de noticias: 

```bash
rails t test/controllers/publications_controller_test.rb -n test_should_get_new
```


```ruby
test "should get new" do
    sign_in users(:one) 
    get new_publication_url
    assert_response :success
    assert_equal 200, response.status
    assert_equal "utf-8", response.charset
end
```

Test para la acción **create** de noticias:

```bash
rails t test/controllers/publications_controller_test.rb -n test_should_create_publication
```

```ruby
test "should get new" do
    sign_in @admin
    get new_publication_url
    assert_response :success
    assert_equal 302, response.status
    assert_equal "utf-8", response.charset
end
```

Test para la acción **show** de noticias:

```bash
rails t test/controllers/publications_controller_test.rb -n test_should_show_publication
```

```ruby
test "should show publication" do
    get publication_url(@publication)
    assert_response :success
    assert_equal 200, response.status
    assert_equal "utf-8", response.charset
end
```


Test para la acción **edit** de noticias:

```bash
rails t test/controllers/publications_controller_test.rb -n test_should_get_edit
```

```ruby
test "should get edit" do
    sign_in @admin
    get edit_publication_url(@publication)
    assert_response :success
    assert_equal 200, response.status
    assert_equal "utf-8", response.charset
end
```

Test para la acción **update** de noticias:

```bash
rails t test/controllers/publications_controller_test.rb -n test_should_update_publication
```

```ruby
test "should update publication" do
    sign_in @admin
    patch publication_url(@publication), params: { publication: { description: @publication.description, title: @publication.title, user_id: @publication.user_id } }
    assert_redirected_to publication_url(@publication)
end
```

Test para la acción **destroy** de noticias:

```bash
rails t test/controllers/publications_controller_test.rb -n test_should_destroy_publication
```

```ruby
test "should destroy publication" do
    sign_in @admin
    assert_difference("Publication.count", -1) do
      delete publication_url(@publication)
    end

    assert_redirected_to publications_url
end
```



## Requerimiento 5

Hacer deploy del proyecto.

Deploy: