Back-End

10 jan, 2017

Coherence e ExAdmin – Devise e ActiveAdmin para Phoenix

Publicidade

Este artigo é para os programadores Ruby que buscam a possibilidade de substituir um pouco de Ruby e Rails por Elixir e Phoenix.

Indo direto ao ponto, muitas aplicações pequenas em Ruby que eu desenvolvi começam com dois add-ons simples: Devise, para autenticação e ActiveAdmin para gerenciamento básico de bancos de dados. Então eu começava daí.

Tanto o Elixir quanto o Phoenix estão mudando rápido, por isso, é difícil para um conjunto estável de bibliotecas se solidificar, mas eu acho que finalmente estamos saindo da curva dos early adopters.

Um grande ponto de contenção tem sido a autenticação de usuários. Muitos tradicionalistas vão dizer que você tem que criar o seu do zero ou utilizando bibliotecas de baixo nível, como o Guardian.

Se você estiver criando uma aplicação que somente exiba saídas de APIs, provavelmente está ok. Mas para uma aplicação web completa desenvolvida para utilização de humanos, essa dificilmente será uma boa escolha. Não vou entrar nessa discussão hoje, pois ela está fora do contexto.

Estou assumindo que você ao menos viu os tutoriais de Elixir e Phoenix. Se ainda não viu, vá em frente e leia, você vai levar 1 ou 2 dias para entender o básico, se você já for um desenvolvedor Ruby experiente. Depois volte e leia meus artigos sobre Elixir para entender como ele é em comparação aos outros.

Tendo dito isso, vamos começar.

Coherence (Alternativa para o Devise)

Finalmente, eu encontrei esse projeto que esteve em um desenvolvimento pesado pelos últimos 6 meses chamado Coherence. Para todos os propósitos, ele imita o Devise em quase tudo. E isso é muito bom para muitos cenários.

Seu README é bem explicado o suficiente, então não vou copiar e colar ele aqui, basta ler e começar a utilizar. Mas se você quiser testar todas suas funcionalidades, você pode ajustar seus procedimentos com essas opções durante a instalação:

mix coherence.install --full --rememberable --invitable --trackable

Execute mix help coherence.install para ver a descrição de todas as opções.

E se você não estiver ajustando o front-end, você pode simplesmente adicionar os links corretos de Cadastro, sign in e sign out, adicionando o fragmento de código abaixo ao caminho web/templates/layout/app.html.eex

<header class="header">
  <nav role="navigation">
    <ul class="nav nav-pills pull-right">
      <%= if Coherence.current_user(@conn) do %>
        <%= if @conn.assigns[:remembered] do %>
          <li style="color: red;">!!</li>
        <% end %>
      <% end %>
      <%= YourApp.Coherence.ViewHelpers.coherence_links(@conn, :layout) %>
      <li><a href="http://www.phoenixframework.org/docs">Get Started</a></li>
    </ul>
  </nav>
  <span class="logo"></span>
</header>
...

(A propósito, sempre que você ver “YourApp” nos fragmentos de código, você deve alterar para o nome do módulo de seu aplicativo.)

Se você se perder na documentação você pode olhar o repositório Demo deles para ver o exemplo de um aplicativo básico Phoenix com o Coherece já configurado e funcionando. Você terá que tomar muito cuidado com o “web/router.ex” para criar o “:protected pipeline” e configurar os escopos corretamente.

Se você fizer corretamente, isso é o que aparecerá:

01

02

 

Fazia muito tempo que eu não me animava com uma simples página de Log In

ExAdmin (Alternativa para o ActiveAdmin)

Então, o próximo passo é adicionar uma interface simples de administração. Para isso eu encontrei o ExAdmin, que está em um desenvolvimento pesado desde maio de 2015. É tão parecido com o ActiveAdmin que o seu tema fará você esquecer que não está em uma aplicação Rails.

Novamente, é bem direto para configurar somente seguindo as instruções de seu README.

Assim que você tiver instalado e configurado, você pode rapidamente exibir o modelo de usuário na interface do Admin assim:

mix admin.gen.resource User

E podemos editar o web/admin/user.ex com o seguinte:

defmodule YourApp.ExAdmin.User do
  use ExAdmin.Register

  register_resource YourApp.User do
    index do
      selectable_column

      column :id
      column :name
      column :email
      column :last_sign_in_at
      column :last_sign_in_ip
      column :sign_in_count
    end

    show _user do
      attributes_table do
        row :id
        row :name
        row :email
        row :reset_password_token
        row :reset_password_sent_at
        row :locked_at
        row :unlock_token
        row :sign_in_count
        row :current_sign_in_at
        row :last_sign_in_at
        row :current_sign_in_ip
        row :last_sign_in_ip
      end
    end

    form user do
      inputs do
        input user, :name
        input user, :email
        input user, :password, type: :password
        input user, :password_confirmation, type: :password
      end
    end
  end
end

Sim, é sinistramente similar ao ActiveAdmin DSL. Ponto positivo para a equipe responsável, e isso realmente mostra como o Elixir funciona bem para Linguagens de Domínio Específico, se você está nessa.

Se você seguiu as intruções do Coherence, ele pede para você adicionar um “:protected pipeline”(uma coleção de conexões) para suas rotas protegidas. Por enquanto você pode adicionar a rota “/admin” para passar por esse canal. E para os “Não iniciados”, essas “conexões” são similares ao conceito do aplicativo Rack, ou mais especificamente, um middleware de Rails. Mas em Rails, nós temos apenas um canal de middlewares. No Phoenix, podemos configurar múltiplos canais para diferentes tipos de rotas (navegador e API, por exemplo).

Então, podemos adicionar o seguinte ao “web/router.ex”

...
scope "/admin", ExAdmin do
  pipe_through :protected
  admin_routes
end
...

Com esses simples dispositivos configurados, você terá algo assim:

03

E se você ainda não estiver convencido, que tal mudar o tema antigo?

04

Agora sim! Faz eu me sentir em casa, embora eu realmente prefira o tema novo. Mas você pode substituir seu aplicativo baseado no ActiveAdmin por esse e seus usuários dificilmente notarão as pequenas diferenças na interface. Os comportamentos são praticamente os mesmos.

Se você ainda tem perguntas sobre como configurar corretamente o ExAdmin, verifique o projeto Contact Demo deles, onde você pode encontrar um exemplo real.

Criação de uma função de administrador simples

Obviamente, não queremos deixar todos os usuários logados acessarem a sessão de Admin.

Então, podemos adicionar um campo booleano simples na tabela de usuários para indicar se ele é administrador ou não. Você pode mudar sua migração para:

...
def change do
  create table(:users, primary_key: false) do

    add :name, :string
    add :email, :string
    ...
    add :admin, :boolean, default: false
    ...
  end
end
...

E você pode configurar o arquivo “priv/repos/seeds.exs” para criar dois usuários, um administrador e um convidado:

YourApp.Repo.delete_all YourApp.User

YourApp.User.changeset(%YourApp.User{}, %{name: "Administrator", email: "admin@example.org", password: "password", password_confirmation: "password", admin: true})
|> YourApp.Repo.insert!

YourApp.User.changeset(%YourApp.User{}, %{name: "Guest", email: "guest@example.org", password: "password", password_confirmation: "password", admin: false})
|> YourApp.Repo.insert!

Como isso é somente um exercício, você pode apagar o banco de dados e criar novamente, assim: mix do ecto.drop, ecto.setup.

O Coherence cuida da autenticação, mas temos que cuidar da autorização. Você vai encontrar muitos exemplos online para algo que lembre o Pundit do Rails, como o Bodyguard. Mas para esse post vou ficar com uma conexão simples e criar um novo canal.

Vamos precisar criar o arquivo “lib/your_app/plugs/authorized.ex” e adicionar o seguinte:

defmodule YourApp.Plugs.Authorized do
  @behaviour Plug

  import Plug.Conn
  import Phoenix.Controller

  def init(default), do: default

  def call(%{assigns: %{current_user: current_user}} = conn, _) do
    if current_user.admin do
      conn
    else
      conn
        |> flash_and_redirect
    end
  end

  def call(conn, _) do
    conn
      |> flash_and_redirect
  end

  defp flash_and_redirect(conn) do
    conn
      |> put_flash(:error, "You do not have the proper authorization to do that")
      |> redirect(to: "/")
      |> halt
  end
end

Assim que o usuário entrar, o Coherence coloca a estrutura de usuário autenticado no “conn” (uma estrutura Plug.Conn), para que possamos combinar os padrões.

Agora, precisamos criar o canal em “web/router.ex” assim:

...
pipeline :protected_admin do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_flash
  plug :protect_from_forgery
  plug :put_secure_browser_headers
  plug Coherence.Authentication.Session, protected: true
  plug YourApp.Plugs.Authorized
end
...
scope "/" do
  pipe_through :protected_admin
  coherence_routes :protected_admin
end
...
scope "/admin", ExAdmin do
  pipe_through :protected_admin
  admin_routes
end
...

O canal “:protected_admin” é exatamente o mesmo de “:protected” mas nós adicionamos o recém criado YourApp.Plugs.Authorized no fim. E então nós mudamos o escopo do “/admin” para passar por esse canal.

É isso. Se você entrar com o usuário “guest@example.org”, ele vai ser enviado para a página inicial com uma mensagem dizendo que ele não é autorizado. Se você entrar com o usuário “admin@example.org”, você poderá acessar a interface ExAdmin em /admin.

Finalizando

Mesmo que agora seja super simples adicionar a autenticação, administração e autorização básica, não se engane, a curva de aprendizado ainda é íngreme, mesmo que você seja desenvolvedor Rails há um tempo.

Devido ao que está por baixo, a arquitetura OTP, os conceitos de aplicações supervisores, trabalhadores, etc, não é imediatamente simples de entender o que está realmente acontecendo. Se você não for cuidadoso, as bibliotecas com o Coherence ou ExAdmin vão fazer você achar que é tão simples quanto em Rails.

E não é assim. Elixir é completamente diferente. E eu digo que é de uma maneira ruim, é exatamente o contrário. É desenvolvido para sistemas distribuídos e altamente confiáveis e demanda muito mais conhecimento, paciência e treinamento do programador.

Por outro lado, exatamente por causa das bibliotecas como o Coherence que tornam mais fácil o início, você pode ficar mais motivado a criar alguma coisa e colocar para rodar, e então investir mais tempo realmente entendendo o que acontece por baixo. Então, a recomendação é: suje suas mãos, tenha alguma gratificação rápida de ver algo rodando, então vá em frente e refine seu conhecimento. Será mais gratificante se você fizer assim.

Eu não vejo o Phoenix sendo somente um substituto para Rails. Seria fácil demais. Eu vejo mais como uma outra peça para que o Elixir seja a melhor escolha para construirmos sistemas altamente distribuídos, altamente confiáveis e altamente escaláveis. Para em simples aplicações web não utilizaria todo o potencial do Elixir.

 

***

Artigo traduzido com autorização do autor. Publicado originalmente em  http://www.akitaonrails.com/2016/12/06/coherence-and-exadmin-devise-and-activeadmin-for-phoenix.