I followed the complete tutorial explaining how to handle context & authentication/authorization by @chrismccord ...and I took advantage of his post about decoupling auth from dockyard blog :slight_smile: auth for phoenix context and from Adding CMS functions in phoenix context
According to this, In my case( CMS is ENR ) all created students need an enroller, the enroller is linked to a user(authenticated) with its own session : Student ->belongs_to Enroller, Enroller ->belongs_to User, users have their own credentials.
the resources "/students", StudentController
is protected that's fine.
But Now I want to expose some fact: what about if we want to allow student registering himself/herself? how the code will look like?
for self-enrollment or self-registration instead of doing it by the admin's folk... it'll be good.
here is what I did but I'm always redirect to the authentication page, the result: "you have to be logged in".
when the Admin is logged in/registered.... S/he can achieve administrative tasks (create: Students, Enrollers, pages, etc.) that's allowed by:
scope "/enr", HelloWeb.ENR, as: :enr do pipe_through [:browser, :authenticate_user] resources "/admissions", AdmissionController end
for the admission's tasks
- In the mentioned case earlier if we consider that scope
CMS
or (ENR
) is the admin part and we want to allow students to register themselves through public part I did route like that:
scope "/", InsWeb do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
resources "/users", UserController
resources "/sessions", SessionController, only: [:new, :create, :delete],
singleton: true
resources "/admissions", AdmissionController, only: [:new, :create, :show]
end
scope "/ENR", InsWeb.ENR, as: :enr do
pipe_through [:browser, :authenticate_user]
resources "/admissions", AdmissionController
end
defp authenticate_user(conn, _) do
case get_session(conn, :user_id) do
nil ->
conn
|> Phoenix.Controller.put_flash(:error, "Login required")
|> Phoenix.Controller.redirect(to: "/")
|> halt()
user_id ->
assign(conn, :current_user, Ins.Accounts.get_user!(user_id))
end
end
all seems good I reached the admission page through public part but when I submitted the form ==== nothing..I always got "Login required" so it's not possible to allow student self-registration...
the
AdmissionController.ex
in the CMS(ENRin my case) scope looks like:defmodule InsWeb.ENR.AdmissionController do use InsWeb, :controller plug :require_existing_enroller plug :authorize_admission when action in [:edit, :update, :delete] alias Ins.ENR alias Ins.ENR.Admission def index(conn, _params) do admissions = ENR.list_admissions() render(conn, "index.html", admissions: admissions) end def new(conn, _params) do changeset = ENR.change_admission(%Admission{}) render(conn, "new.html", changeset: changeset) end def create(conn, %{"admission" => admission_params}) do case ENR.create_admission(conn.assigns.current_enroller, admission_params) do {:ok, admission} -> conn |> put_flash(:info, "Admission created successfully.") |> redirect(to: enr_admission_path(conn, :show, admission)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end def show(conn, %{"id" => id}) do admission = id |> ENR.get_admission!() |> ENR.inc_admission_views() render(conn, "show.html", admission: admission) end def edit(conn, %{"id" => id}) do admission = ENR.get_admission!(id) changeset = ENR.change_admission(admission) render(conn, "edit.html", admission: admission, changeset: changeset) end def update(conn, %{"id" => id, "admission" => admission_params}) do admission = ENR.get_admission!(id) case ENR.update_admission(conn.assigns.admission, admission_params) do {:ok, admission} -> conn |> put_flash(:info, "Admission updated successfully.") |> redirect(to: enr_admission_path(conn, :show, admission)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "edit.html", admission: admission, changeset: changeset) end end def delete(conn, %{"id" => id}) do admission = ENR.get_admission!(id) {:ok, _admission} = ENR.delete_admission(conn.assigns.admission) conn |> put_flash(:info, "Admission deleted successfully.") |> redirect(to: enr_admission_path(conn, :index)) end defp require_existing_enroller(conn, _) do enroller = ENR.ensure_enroller_exists(conn.assigns.current_user) assign(conn, :current_enroller, enroller) end defp authorize_admission(conn, _) do admission = ENR.get_admission!(conn.params["id"]) if conn.assigns.current_enroller.id == admission.enroller_id do assign(conn, :admission, admission) else conn |> put_flash(:error, "You can't modify that admission page") |> redirect(to: enr_admission_path(conn, :index)) |> halt() end end end
And the other one out of CMS
defmodule InsWeb.AdmissionController do
use InsWeb, :controller
alias Ins.ENR
alias Ins.ENR.Admission
def new(conn, _params) do
changeset = ENR.change_admission(%Admission{})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"admission" => admission_params}) do
case ENR.create_admission(conn.assigns.current_enroller, admission_params) do
{:ok, admission} ->
conn
|> put_flash(:info, "Admission created successfully.")
|> redirect(to: enr_admission_path(conn, :show, admission))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
def show(conn, %{"id" => id}) do
admission =
id
|> ENR.get_admission!()
render(conn, "show.html", admission: admission)
end
end
Any help would be appreciated! thank you!
Here are the other contents
/templates/enr/admission/form.html.eex
<%= form_for @changeset, @action, fn f -> %> <%= if @changeset.action do %> <div class="alert alert-danger"> <p>Oops, something went wrong! Please check the errors below.</p> </div> <% end %> <div class="form-group"> <%= label f, :first_name, class: "control-label" %> <%= text_input f, :first_name, class: "form-control" %> <%= error_tag f, :first_name %> </div> <div class="form-group"> <%= label f, :last_name, class: "control-label" %> <%= text_input f, :last_name, class: "form-control" %> <%= error_tag f, :last_name %> </div> <div class="form-group"> <%= label f, :views, class: "control-label" %> <%= number_input f, :views, class: "form-control" %> <%= error_tag f, :views %> </div> <div class="form-group"> <%= submit "Submit", class: "btn btn-primary" %> </div> <% end %>
/templates/enr/admission/new.html.eex
<h2>New Admission</h2> <%= render "form.html", Map.put(assigns, :action, enr_admission_path(@conn, :create)) %> <span><%= link "Back", to: enr_admission_path(@conn, :index) %></span>