翻蚳è ããïŒã ãšãªã¯ãµãŒãšãã§ããã¯ã¹ã¯ãææ°ã®ãŠã§ãéçºãã©ãã«é²ãã§ãããã®è¯ãäŸã§ãã ãã§ã«ãããã®ããŒã«ã¯ãWebã¢ããªã±ãŒã·ã§ã³ã®ãªã¢ã«ã¿ã€ã ãã¯ãããžãŒãžã®è³ªã®é«ãã¢ã¯ã»ã¹ãæäŸããŸãã 察話æ§ãåäžãããµã€ãããã«ããŠãŒã¶ãŒãã©ãŠã¶ãŒã²ãŒã ããã€ã¯ããµãŒãã¹ã¯ããããã®æè¡ãããŸãæ©èœããåéã§ãã 以äžã¯ãPhoenixãã¬ãŒã ã¯ãŒã¯ã§ã®éçºã®è©³çŽ°ãªåŽé¢ã説æããäžé£ã®11ã®èšäºã®ç¿»èš³ã§ããããã°ãšã³ãžã³ã®ãããªäºçŽ°ãªããšã®ããã«æããŸãã ããããæ¥ãã§ã€ãŸãããªãã§ãã ãããç¹ã«èšäºããšãªã¯ãµãŒã«æ³šæãæããã圌ã®ãã©ãã¯ãŒã«ãªãããã«ä¿ãå Žåãããã¯æ¬åœã«é¢çœãã§ãããã
ãã®ããŒãã§ã¯ãããŒã«ã䜿çšããŠã¢ã¯ã»ã¹æš©ã®åé¢ãå®äºããŸãã ãã®ã·ãªãŒãºã®èšäºã®éèŠãªãã€ã³ãã¯ããã¹ãã«å€ãã®æ³šæãæãããŠããããšã§ããããã¹ãã¯çŽ æŽãããã§ãïŒ
çŸæç¹ã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã¯ä»¥äžã«åºã¥ããŠããŸãã
- ãšãªã¯ãµãŒ ïŒv1.3.1
- ãã§ããã¯ã¹ ïŒv1.2.0
- EctoïŒv2.0.2
- ã³ã¡ãã³ ïŒv2.5.2
ç§ãã¡ãæ³ãŸã£ãå Žæ
ååã¯ãã¢ãã«ã®å éšã«ããŒã«ã®æŠå¿µãè¿œå ãããã¹ããç°¡åã«ããããã«ãã¹ãçšã®è£å©é¢æ°ãäœæããæ¹æ³ãåããŸããã 次ã«ãããŒã«ããŒã¹ã®å¶çŽãã³ã³ãããŒã©ãŒã«è¿œå ããå¿ èŠããããŸãã ä»»æã®ã³ã³ãããŒã©ãŒã§äœ¿çšã§ãããã«ããŒé¢æ°ãäœæããããšããå§ããŸãããã
ããŒã«ããã§ãã¯ããããã®ãã«ããŒé¢æ°ãäœæãã
ä»æ¥ã®æåã®ã¹ãããã¯ã管çè æš©éã®ç°¡åãªãŠãŒã¶ãŒãã§ãã¯ãäœæããããšã§ãã ãããè¡ãã«ã¯ã
web/models/role_checker.ex
ãäœæãã次ã®ã³ãŒããå ¥åããŸãïŒ
defmodule Pxblog.RoleChecker do alias Pxblog.Repo alias Pxblog.Role def is_admin?(user) do (role = Repo.get(Role, user.role_id)) && role.admin end end
ãŸãããã®æ©èœãã«ããŒãããã¹ããäœæããŠã¿ãŸãããã ãã¡ã€ã«
test/models/role_checker_test.exs
éããŸãïŒ
defmodule Pxblog.RoleCheckerTest do use Pxblog.ModelCase alias Pxblog.TestHelper alias Pxblog.RoleChecker test "is_admin? is true when user has an admin role" do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, user} = TestHelper.create_user(role, %{email: "test@test.com", username: "user", password: "test", password_confirmation: "test"}) assert RoleChecker.is_admin?(user) end test "is_admin? is false when user does not have an admin role" do {:ok, role} = TestHelper.create_role(%{name: "User", admin: false}) {:ok, user} = TestHelper.create_user(role, %{email: "test@test.com", username: "user", password: "test", password_confirmation: "test"}) refute RoleChecker.is_admin?(user) end end
æåã®ãã¹ãã§ã¯ç®¡çè ãäœæãã2çªç®ã®ãã¹ãã§ã¯éåžžã®ãŠãŒã¶ãŒã§ãã æåŸã«ãé¢æ°
is_admin?
ã確èªã
is_admin?
æåã®
true
ã¯
true
ã2çªç®ã®å Žåã¯
false
ãè¿ã
true
ã
is_admin?
é¢æ°ã¯
is_admin?
RoleChecker
ã¢ãžã¥ãŒã«ãããŠãŒã¶ãŒãå¿ èŠãªå Žåãããã©ãŒãã³ã¹ããã§ãã¯ããéåžžã«ç°¡åãªãã¹ããäœæã§ããŸãã ç§ãã¡ã確信ã§ããã³ãŒããå€æããŸããïŒ ãã¹ããå®è¡ããç·è²ã®ãŸãŸã§ããããšã確èªããŸãã
管çè ã®ã¿ã«ãŠãŒã¶ãŒã®è¿œå ãèš±å¯ãã
以åã¯ã
UserController
ã«å¶éãè¿œå ããªãã£ããããä»ã
authorize_user
ãæå¹ã«ãããšãã§ãã ããããäœãããããããã«èšç»ããŸãããã ãŠãŒã¶ãŒã¯èªåã®ãããã¡ã€ã«ãç·šéãæŽæ°ãåé€ã§ããŸãããæ°ãããŠãŒã¶ãŒãè¿œå ã§ããã®ã¯ç®¡çè ã ãã§ãã
web/controllers/user_controller.ex
scrub_params
web/controllers/user_controller.ex
scrub_params
è¡ã®äžã«ã次ãè¿œå ããŸãã
plug :authorize_admin when action in [:new, :create] plug :authorize_user when action in [:edit, :update, :delete]
ãŸãããã¡ã€ã«ã®äžéšã«ããŠãŒã¶ãŒèªèšŒãšç®¡çè èªèšŒãåŠçãããã©ã€ããŒãé¢æ°ãããã€ãè¿œå ããŸãã
defp authorize_user(conn, _) do user = get_session(conn, :current_user) if user && (Integer.to_string(user.id) == conn.params["id"] || Pxblog.RoleChecker.is_admin?(user)) do conn else conn |> put_flash(:error, "You are not authorized to modify that user!") |> redirect(to: page_path(conn, :index)) |> halt() end end defp authorize_admin(conn, _) do user = get_session(conn, :current_user) if user && Pxblog.RoleChecker.is_admin?(user) do conn else conn |> put_flash(:error, "You are not authorized to create new users!") |> redirect(to: page_path(conn, :index)) |> halt() end end
authorize_user
åŒã³åºãã¯ã
PostController
ããã§ãã¯ããããšãé€ããŠã
PostController
ã«ãã£ããã®ãšæ¬è³ªçã«åã
RoleChecker.is_admin?
ã
authorize_admin
é¢æ°ã¯ããã«ã·ã³ãã«ã§ãã çŸåšã®ãŠãŒã¶ãŒã管çè ã§ããããšã®ã¿ã確èªããŸãã
ãã¡ã€ã«
test/controllers/user_controller_test.exs
æ»ã£ãŠãæ°ããæ¡ä»¶ãèæ ®ã«å ¥ããããã«ãã¹ããå€æŽããŸãããã
ã»ããã¢ãããããã¯ãå€æŽããããšããå§ããŸãããã
setup do {:ok, user_role} = TestHelper.create_role(%{name: "user", admin: false}) {:ok, nonadmin_user} = TestHelper.create_user(user_role, %{email: "nonadmin@test.com", username: "nonadmin", password: "test", password_confirmation: "test"}) {:ok, admin_role} = TestHelper.create_role(%{name: "admin", admin: true}) {:ok, admin_user} = TestHelper.create_user(admin_role, %{email: "admin@test.com", username: "admin", password: "test", password_confirmation: "test"}) {:ok, conn: build_conn(), admin_role: admin_role, user_role: user_role, nonadmin_user: nonadmin_user, admin_user: admin_user} end
ãã®å éšã«ãŠãŒã¶ãŒããŒã«ã管çè ã®ããŒã«ãéåžžã®ãŠãŒã¶ãŒããã³ç®¡çè ãäœæããããããè¿ããŸãã ãããã£ãŠããµã³ãã«ãšæ¯èŒããããšã§ããã¹ãã§ãããã䜿çšããæ©äŒãåŸãããŸãã ãã°ã€ã³ããããã®ãã«ããŒé¢æ°ãå¿ èŠã«ãªãããã
login_user
ãã
PostController
é¢æ°ãã³ããŒããŸãã
defp login_user(conn, user) do post conn, session_path(conn, :create), user: %{username: user.username, password: user.password} end
index
ã¢ã¯ã·ã§ã³ã«å¶éãè¿œå ããªãããããã®ãã¹ããã¹ãããã§ããŸãã 次ã®ãã¹ããæ°ãããªãœãŒã¹ã®ãã©ãŒã ãã¬ã³ããªã³ã°ãããïŒ
new
ã¢ã¯ã·ã§ã³ãè¡šãïŒã§ã¯ãå¶éã課ãããŸãã ãŠãŒã¶ãŒã«ã¯ç®¡çè æš©éãå¿ èŠã§ãã
次ã®ã³ãŒãã«äžèŽããããã«ãã¹ããå€æŽããŸãã
@tag admin: true test "renders form for new resources", %{conn: conn, admin_user: admin_user} do conn = conn |> login_user(admin_user) |> get(user_path(conn, :new)) assert html_response(conn, 200) =~ "New user" end
ãã®ãã¹ãã®äžã«
@tag admin: true
ãè¿œå ããŠã
@tag admin: true
ãšããŠããŒã¯ããŸãã ãããã£ãŠãã»ããå šäœã§ã¯ãªããåæ§ã®ãã¹ãã®ã¿ãå®è¡ã§ããŸãã è©ŠããŠã¿ãŸãããïŒ
mix test --only admin
åºåã§ã¯ããšã©ãŒã衚瀺ãããŸãã
1) test renders form for new resources (Pxblog.UserControllerTest) test/controllers/user_controller_test.exs:26 ** (KeyError) key :role_id not found in: %{id: 348, username: âadminâ} stacktrace: (pxblog) web/models/role_checker.ex:6: Pxblog.RoleChecker.is_admin?/1 (pxblog) web/controllers/user_controller.ex:84: Pxblog.UserController.authorize_admin/2 (pxblog) web/controllers/user_controller.ex:1: Pxblog.UserController.phoenix_controller_pipeline/2 (pxblog) lib/phoenix/router.ex:255: Pxblog.Router.dispatch/2 (pxblog) web/router.ex:1: Pxblog.Router.do_call/2 (pxblog) lib/pxblog/endpoint.ex:1: Pxblog.Endpoint.phoenix_pipeline/1 (pxblog) lib/phoenix/endpoint/render_errors.ex:34: Pxblog.Endpoint.call/2 (phoenix) lib/phoenix/test/conn_test.ex:193: Phoenix.ConnTest.dispatch/5 test/controllers/user_controller_test.exs:28
ããã§ã®åé¡ã¯ãå®å šãªãŠãŒã¶ãŒã¢ãã«ã
RoleChecker.is_admin?
é¢æ°ã«æž¡ããŠããªãããš
RoleChecker.is_admin?
ã ãããŠã
sign_in
ã¢ãžã¥ãŒã«ã®
sign_in
é¢æ°ãã
current_user
é¢æ°ãåãåã£ãããŒã¿ã®å°ããªãµãã»ããã
sign_in
ãŸãã
åæ§ã«
role_id
ãè¿œå ããŸãããã 以äžã«ç€ºãããã«ã
web/controllers/session_controller.ex
ãã¡ã€ã«ã«å€æŽãå ããŸããã
defp sign_in(user, password, conn) do if checkpw(password, user.password_digest) do conn |> put_session(:current_user, %{id: user.id, username: user.username, role_id: user.role_id}) |> put_flash(:info, "Sign in successful!") |> redirect(to: page_path(conn, :index)) else failed_login(conn) end end
ããäžåºŠã
admin
ã¿ã°ã䜿çšããŠãã¹ããå®è¡ããŠã¿ãŸãããã
$ mix test --only admin
åã³ç·ïŒ ãŠãŒã¶ãŒã管çè ã§ã¯ãªãããåæã«
UserController
æ°ããã¢ã¯ã·ã§ã³ãå ¥åããããšããå Žåãéã®ç¶æ³ã®ãã¹ããäœæããå¿ èŠããããŸãã ãã¡ã€ã«
test/controllers/user_controller_test.exs
æ»ããŸãã
@tag admin: true test "redirects from new form when not admin", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :new) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end
create
ã¢ã¯ã·ã§ã³ã«ã€ããŠãåãããšãè¡ã
create
ã äž¡æ¹ã®ã±ãŒã¹ã«å¯ŸããŠ1ã€ã®ãã¹ããäœæããŸãã
@tag admin: true test "creates resource and redirects when data is valid", %{conn: conn, user_role: user_role, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert redirected_to(conn) == user_path(conn, :index) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "redirects from creating user when not admin", %{conn: conn, user_role: user_role, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "does not create resource and renders errors when data is invalid", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: @invalid_attrs assert html_response(conn, 200) =~ "New user" end
show
ã¢ã¯ã·ã§ã³ãã¹ãããã§ããŸã 圌ã«æ°ããæ¡ä»¶ãè¿œå ããŸããã§ããã
user_controller_test.exs
ãã¡ã€ã«ã次ã®ããã«ãªããŸã§ãåããã¿ãŒã³ã«åŸããŸãã
defmodule Pxblog.UserControllerTest do use Pxblog.ConnCase alias Pxblog.User alias Pxblog.TestHelper @valid_create_attrs %{email: "test@test.com", username: "test", password: "test", password_confirmation: "test"} @valid_attrs %{email: "test@test.com", username: "test"} @invalid_attrs %{} setup do {:ok, user_role} = TestHelper.create_role(%{name: "user", admin: false}) {:ok, nonadmin_user} = TestHelper.create_user(user_role, %{email: "nonadmin@test.com", username: "nonadmin", password: "test", password_confirmation: "test"}) {:ok, admin_role} = TestHelper.create_role(%{name: "admin", admin: true}) {:ok, admin_user} = TestHelper.create_user(admin_role, %{email: "admin@test.com", username: "admin", password: "test", password_confirmation: "test"}) {:ok, conn: build_conn(), admin_role: admin_role, user_role: user_role, nonadmin_user: nonadmin_user, admin_user: admin_user} end defp valid_create_attrs(role) do Map.put(@valid_create_attrs, :role_id, role.id) end defp login_user(conn, user) do post conn, session_path(conn, :create), user: %{username: user.username, password: user.password} end test "lists all entries on index", %{conn: conn} do conn = get conn, user_path(conn, :index) assert html_response(conn, 200) =~ "Listing users" end @tag admin: true test "renders form for new resources", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = get conn, user_path(conn, :new) assert html_response(conn, 200) =~ "New user" end @tag admin: true test "redirects from new form when not admin", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :new) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "creates resource and redirects when data is valid", %{conn: conn, user_role: user_role, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert redirected_to(conn) == user_path(conn, :index) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "redirects from creating user when not admin", %{conn: conn, user_role: user_role, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "does not create resource and renders errors when data is invalid", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: @invalid_attrs assert html_response(conn, 200) =~ "New user" end test "shows chosen resource", %{conn: conn} do user = Repo.insert! %User{} conn = get conn, user_path(conn, :show, user) assert html_response(conn, 200) =~ "Show user" end test "renders page not found when id is nonexistent", %{conn: conn} do assert_error_sent 404, fn -> get conn, user_path(conn, :show, -1) end end @tag admin: true test "renders form for editing chosen resource when logged in as that user", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :edit, nonadmin_user) assert html_response(conn, 200) =~ "Edit user" end @tag admin: true test "renders form for editing chosen resource when logged in as an admin", %{conn: conn, admin_user: admin_user, nonadmin_user: nonadmin_user} do conn = login_user(conn, admin_user) conn = get conn, user_path(conn, :edit, nonadmin_user) assert html_response(conn, 200) =~ "Edit user" end @tag admin: true test "redirects away from editing when logged in as a different user", %{conn: conn, nonadmin_user: nonadmin_user, admin_user: admin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :edit, admin_user) assert get_flash(conn, :error) == "You are not authorized to modify that user!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "updates chosen resource and redirects when data is valid when logged in as that user", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = put conn, user_path(conn, :update, nonadmin_user), user: @valid_create_attrs assert redirected_to(conn) == user_path(conn, :show, nonadmin_user) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "updates chosen resource and redirects when data is valid when logged in as an admin", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = put conn, user_path(conn, :update, admin_user), user: @valid_create_attrs assert redirected_to(conn) == user_path(conn, :show, admin_user) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "does not update chosen resource when logged in as different user", %{conn: conn, nonadmin_user: nonadmin_user, admin_user: admin_user} do conn = login_user(conn, nonadmin_user) conn = put conn, user_path(conn, :update, admin_user), user: @valid_create_attrs assert get_flash(conn, :error) == "You are not authorized to modify that user!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "does not update chosen resource and renders errors when data is invalid", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = put conn, user_path(conn, :update, nonadmin_user), user: @invalid_attrs assert html_response(conn, 200) =~ "Edit user" end @tag admin: true test "deletes chosen resource when logged in as that user", %{conn: conn, user_role: user_role} do {:ok, user} = TestHelper.create_user(user_role, @valid_create_attrs) conn = login_user(conn, user) |> delete(user_path(conn, :delete, user)) assert redirected_to(conn) == user_path(conn, :index) refute Repo.get(User, user.id) end @tag admin: true test "deletes chosen resource when logged in as an admin", %{conn: conn, user_role: user_role, admin_user: admin_user} do {:ok, user} = TestHelper.create_user(user_role, @valid_create_attrs) conn = login_user(conn, admin_user) |> delete(user_path(conn, :delete, user)) assert redirected_to(conn) == user_path(conn, :index) refute Repo.get(User, user.id) end @tag admin: true test "redirects away from deleting chosen resource when logged in as a different user", %{conn: conn, user_role: user_role, nonadmin_user: nonadmin_user} do {:ok, user} = TestHelper.create_user(user_role, @valid_create_attrs) conn = login_user(conn, nonadmin_user) |> delete(user_path(conn, :delete, user)) assert get_flash(conn, :error) == "You are not authorized to modify that user!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end end
ãã¹ãã»ããå šäœãèµ·åããŸãã 圌ãã¯ãã¹ãŠåã³ééããŸãïŒ
管çè ãæçš¿ãå€æŽã§ããããã«ããŸã
幞ããªããšã«ãç§ãã¡ã¯ãã§ã«ã»ãšãã©ã®äœæ¥ãå®äºããŠããããã®æåŸã®éšåã ããæ®ã£ãŠããŸãã äœæ¥ãå®äºãããšã管çè ã®æ©èœãå®å šã«æºåã§ããŸãã
web/controllers/post_controller.ex
ãéãã
authorize_user
é¢æ°ãå€æŽããŠ
RoleChecker.is_admin?
ãã«ããŒé¢æ°
RoleChecker.is_admin?
䜿çšã
RoleChecker.is_admin?
ã ãŠãŒã¶ãŒã管çè ã§ããå ŽåããŠãŒã¶ãŒæçš¿ã®å€æŽãå®å šã«å¶åŸ¡ã§ããŸãã
defp authorize_user(conn, _) do user = get_session(conn, :current_user) if user && (Integer.to_string(user.id) == conn.params["user_id"] || Pxblog.RoleChecker.is_admin?(user)) do conn else conn |> put_flash(:error, "You are not authorized to modify that post!") |> redirect(to: page_path(conn, :index)) |> halt() end end
test/controllers/post_controller_test.exs
ããã¡ã€ã«
test/controllers/post_controller_test.exs
ãæ¿èªã«ãŒã«ãã«ããŒããããã«ããã€ãã®ãã¹ããè¿œå ããŸãã
test "redirects when trying to delete a post for a different user", %{conn: conn, role: role, post: post} do {:ok, other_user} = TestHelper.create_user(role, %{email: "test2@test.com", username: "test2", password: "test", password_confirmation: "test"}) conn = delete conn, user_post_path(conn, :delete, other_user, post) assert get_flash(conn, :error) == "You are not authorized to modify that post!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end test "renders form for editing chosen resource when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> get(user_post_path(conn, :edit, user, post)) assert html_response(conn, 200) =~ "Edit post" end test "updates chosen resource and redirects when data is valid when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> put(user_post_path(conn, :update, user, post), post: @valid_attrs) assert redirected_to(conn) == user_post_path(conn, :show, user, post) assert Repo.get_by(Post, @valid_attrs) end test "does not update chosen resource and renders errors when data is invalid when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> put(user_post_path(conn, :update, user, post), post: %{"body" => nil}) assert html_response(conn, 200) =~ "Edit post" end test "deletes chosen resource when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> delete(user_post_path(conn, :delete, user, post)) assert redirected_to(conn) == user_post_path(conn, :index, user) refute Repo.get(Post, post.id) end
çŸåšãç§ãã¡ã®ããã°ãšã³ãžã³ã¯å šäœãšããŠã¹ã ãŒãºã«åäœããŠããŸãããããã€ãã®ãã°ããããŸãã ãã¶ã圌ãã¯ééã£ãŠçŸããã ãŸãã¯éäžã§äœããå¿ããŸããã ãããã£ãŠãããããèå¥ããŠæé€ããŸãããã ãŸããã¢ããªã±ãŒã·ã§ã³ãå¯èœãªãã¹ãŠã®ææ°ããŒãžã§ã³ã§å®è¡ãããããã«ããã¹ãŠã®äŸåé¢ä¿ãæŽæ°ããŸãããã
æ°ãããŠãŒã¶ãŒãè¿œå ãããšã圹å²ãèŠã€ãããªããšãããšã©ãŒã衚瀺ããã
ããã¯ã nolotusãPxblogããŒãžïŒ https://github.com/Diamond/pxblogïŒã§ çºèŠããŸããã ããããšã
part_3
ãã©ã³ã
part_3
ã¯ãæ°ãããŠãŒã¶ãŒãäœæããããšãããšãããŒã«ããªããããšã©ãŒãçºçããŸãïŒãŠãŒã¶ãŒã®äœææã«
role_id
ã®ååšã
role_id
ã«ããããïŒã æåã«åé¡ã調ã¹ãŠãããä¿®æ£ãå§ããŸãããã 管çè ãšããŠãã°ã€ã³ããã¢ãã¬ã¹
/users/new
ã«ç§»åãããã¹ãŠã®ãã£ãŒã«ãã«å ¥åããŠãã¿ã³ãã¯ãªãã¯ãããšã次ã®ãšã©ãŒã衚瀺ãããŸãã
ããã¯ããŠãŒã¶ãŒã«ååãã¡ãŒã«ããã¹ã¯ãŒãããã¹ã¯ãŒã確èªã®å ¥åãèŠæ±ããããã«èµ·ãããŸãã ãããã圹å²ã«ã€ããŠã¯äœãèšããŸããã ãããç¥ã£ãŠã解決çã«é²ã¿ãŸãããã ãã©ãŒã ã§éžæå¯èœãªããŒã«ã®ãªã¹ããæž¡ãããšããå§ããŸãããã
new, create, edit update
åã¢ã¯ã·ã§ã³ã§å¿ èŠã«ãªã
new, create, edit update
ã
alias Pxblog.Role
ããŸã ååšããªãå Žåã¯ãUserController
alias Pxblog.Role
å é ïŒãã¡ã€ã«
web/controllers/user_controller.ex
alias Pxblog.Role
è¿œå ããŸãã 次ã«ãåè¿°ã®ãã¹ãŠã®ã¢ã¯ã·ã§ã³ã«å€æŽãå ããŸãã
def new(conn, _params) do roles = Repo.all(Role) changeset = User.changeset(%User{}) render(conn, "new.html", changeset: changeset, roles: roles) end def edit(conn, %{"id" => id}) do roles = Repo.all(Role) user = Repo.get!(User, id) changeset = User.changeset(user) render(conn, "edit.html", user: user, changeset: changeset, roles: roles) end def create(conn, %{"user" => user_params}) do roles = Repo.all(Role) changeset = User.changeset(%User{}, user_params) case Repo.insert(changeset) do {:ok, _user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: user_path(conn, :index)) {:error, changeset} -> render(conn, "new.html", changeset: changeset, roles: roles) end end def update(conn, %{"id" => id, "user" => user_params}) do roles = Repo.all(Role) user = Repo.get!(User, id) changeset = User.changeset(user, user_params) case Repo.update(changeset) do {:ok, user} -> conn |> put_flash(:info, "User updated successfully.") |> redirect(to: user_path(conn, :show, user)) {:error, changeset} -> render(conn, "edit.html", user: user, changeset: changeset, roles: roles) end end
ããããã«ã€ããŠã
Repo.all(Role)
ã䜿çšããŠãã¹ãŠã®ããŒã«ãéžæãã
assigns
ãªã¹ãã«è¿œå ããããšã«æ³šæããŠãã ããïŒãšã©ãŒã®å Žåãå«ãïŒã
Phoenix.Html
ãã©ãŒã ã®ãã«ããŒé¢æ°ã䜿çšããŠããããããŠã³ãªã¹ããå®è£ ããå¿ èŠããããŸãã ããã§ã¯ãããã¥ã¡ã³ãã§ãããã©ã®ããã«è¡ããããèŠãŠã¿ãŸãããïŒ
select(form, field, values, opts \\ []) Generates a select tag with the given values.
ããããããŠã³ãªã¹ãã¯ã
values
åŒæ°ãšããŠéåžžã®ãªã¹ãïŒ
[value, value, value]
ã®åœ¢åŒïŒãŸãã¯ããŒã¯ãŒãã®ãªã¹ãïŒ
[displayed: value, displayed: value]
ã®åœ¢åŒïŒã®ãããããæ³å®ããŠã
values
ã ãã®å ŽåãããŒã«åã衚瀺ãããšåæã«ããã©ãŒã ãéä¿¡ãããšãã«éžæããããŒã«ã®èå¥åã®å€ãæž¡ãå¿ èŠããããŸãã ãªã¹ããããŠããã©ã®ãã©ãŒãããã«ãé©åããªãããã
@roles
å€æ°ãåçŽã«ãã«ããŒé¢æ°ã«åçŽã«ã¹ããŒããããšã¯ã§ããŸããã ããã§ã¯ãã¿ã¹ã¯ãç°¡çŽ åããé¢æ°ã
View
ã§æžããŸãããã
defmodule Pxblog.UserView do use Pxblog.Web, :view def roles_for_select(roles) do roles |> Enum.map(&["#{&1.name}": &1.id]) |> List.flatten end end
roles_for_select
é¢æ°ãè¿œå ããŸããã
roles_for_select
é¢æ°ã¯ãåã«ããŒã«ã®ã³ã¬ã¯ã·ã§ã³ãåãå ¥ããŸãã ãã®é¢æ°ã®æ©èœã®è¡ãèŠãŠã¿ãŸãããã é£éã®æ¬¡ã®é¢æ°ã«æž¡ã圹å²ã³ã¬ã¯ã·ã§ã³ããå§ããŸãããã
Enum.map(&["#{&1.name}": &1.id])
&/&1
ã¯å¿åé¢æ°ã®ç¥èªã§ãããå®å šçã§ã¯æ¬¡ã®ããã«æžãæããããšãã§ããããšãæãåºããŠãã ããã
Enum.map(roles, fn role -> ["#{role.name}": role.id] end)
map
æäœãéå§ããŠãããå°ããªããŒãªã¹ãã®ãªã¹ããè¿ããŸãããããŒã«åã¯ããŒã§ãããŒã«èå¥åã¯å€ã§ãã
ããŒã«ã®åæå€ãäžãããããšä»®å®ããŸãïŒ
roles = [%Role{name: "Admin Role", id: 1}, %Role{name: "User Role", id: 2}]
ãã®å Žåããããé¢æ°ãåŒã³åºããšã次ã®ãããªãªã¹ããè¿ãããŸãã
[["Admin Role": 1], ["User Role": 2]]
次ã«ãäžå¿ èŠãªãã¹ããåé€ããæåŸã®
List.flatten
é¢æ°ã«
List.flatten
ãŸãã ãããã£ãŠãæçµçµæã¯æ¬¡ã®ãšããã§ãã
["Admin Role": 1, "User Role": 2]
ããã¯ãããããããŠã³ãªã¹ãã®è£å©æ©èœã«å¿ èŠãªåœ¢åŒã§ããããšãããŸããŸãããŸããïŒ è©ãshoulder
web/templates/user/new.html.eex
ããšã¯ã§ããŸãããã
web/templates/user/new.html.eex
ã®ãã³ãã¬ãŒããå€æŽããå¿ èŠããã
web/templates/user/new.html.eex
ïŒ
<h2>New user</h2> <%= render "form.html", changeset: @changeset, action: user_path(@conn, :create), roles: @roles %> <%= link "Back", to: user_path(@conn, :index) %>
ãããŠã
web/templates/user/edit.html.eex
ïŒ
<h2>Edit user</h2> <%= render "form.html", changeset: @changeset, action: user_path(@conn, :update, @user), roles: @roles %> <%= link "Back", to: user_path(@conn, :index) %>
ãããŠæåŸã«ãæ°ãããã«ããŒé¢æ°ã
web/templates/user/form.html.eex
ã«è¿œå ããããšãæåŠããªããšæã
web/templates/user/form.html.eex
ã ãã®çµæããŠãŒã¶ãŒã翻蚳ã§ãããã¹ãŠã®ããŒã«ãå«ãããããããŠã³ãªã¹ãããã©ãŒã ã«è¡šç€ºãããŸãã [ éä¿¡ ]ãã¿ã³ã®åã«æ¬¡ã®ã³ãŒããè¿œå ããŸãã
<div class="form-group"> <%= label f, :role_id, "Role", class: "control-label" %> <%= select f, :role_id, roles_for_select(@roles), class: "form-control" %> <%= error_tag f, :role_id %> </div>
ããã§ãæ°ãããŠãŒã¶ãŒãè¿œå ããããæ¢åã®ãŠãŒã¶ãŒãç·šéããããšãããšããã®ãŠãŒã¶ãŒã«åœ¹å²ãå²ãåœãŠãæ©äŒãåŸãããŸãïŒ æåŸã®ãã°ãæ®ã£ãŠããŸãïŒ
åæããŒã¿ãããŠã³ããŒããããšãããããé£ç¶ããŠæ°åè€è£œãããŸã
çŸåšãåæããŒã¿ãæ°åããŒããããšãéè€ãçºçããŸãããããã¯ãšã©ãŒã§ãã ãã«ããŒå¿åã®
find_or_create
é¢æ°ãããã€ãæžããŠã¿ãŸãããã
alias Pxblog.Repo alias Pxblog.Role alias Pxblog.User import Ecto.Query, only: [from: 2] find_or_create_role = fn role_name, admin -> case Repo.all(from r in Role, where: r.name == ^role_name and r.admin == ^admin) do [] -> %Role{} |> Role.changeset(%{name: role_name, admin: admin}) |> Repo.insert!() _ -> IO.puts "Role: #{role_name} already exists, skipping" end end find_or_create_user = fn username, email, role -> case Repo.all(from u in User, where: u.username == ^username and u.email == ^email) do [] -> %User{} |> User.changeset(%{username: username, email: email, password: "test", password_confirmation: "test", role_id: role.id}) |> Repo.insert!() _ -> IO.puts "User: #{username} already exists, skipping" end end _user_role = find_or_create_role.("User Role", false) admin_role = find_or_create_role.("Admin Role", true) _admin_user = find_or_create_user.("admin", "admin@test.com", admin_role)
ãšã€ãªã¢ã¹
Repo
ã
Role
ããã³
User
ãè¿œå ãããŠããããšã«æ³šæããŠãã ããã ãŸãã
Ecto.Query
ã¢ãžã¥ãŒã«
from
é¢æ°
from
ã€ã³ããŒãããŠã䟿å©ãªã¯ãšãªæ§æã䜿çšããŸãã 次ã«ãå¿åã®
find_or_create_role
é¢æ°ãèŠãŠãã ãããé¢æ°èªäœã¯ãåã«åŒæ°ãšããŠããŒã«åãšç®¡çãã©ã°ãåãå ¥ããŸãã
ãããã®åºæºã«åºã¥ããŠãã¯ãšãªãå®è¡ããŸã
Repo.all
ïŒæ¡ä»¶ãšäžèŽããã®ã§ã¯ãªããå€ãæ¯èŒãããããæ¡ä»¶å ã®åå€æ°ã«ç¶ã^èšå·ã«æ³šæããŠãã ãã
where
ïŒããããŠãçµæãcaseã¹ããŒãã¡ã³ãã«ã¹ããŒããŸãã
Repo.all
äœãèŠã€ãããªãã£ãå Žåã¯ã空ã®ãªã¹ããè¿ããããããããŒã«ãè¿œå ããå¿ èŠããããŸãããã以å€ã®å Žåã¯ãããŒã«ãæ¢ã«ååšãããšæ³å®ãããã¡ã€ã«ã®æ®ãã®ããŠã³ããŒãã«é²ã¿ãŸãããã®é¢æ°
find_or_create_user
ã¯åãããšãè¡ããŸãããç°ãªãåºæºã䜿çšããŸãã
æåŸã«ããããã®åé¢æ°ãåŒã³åºããŸãïŒååãšåŒæ°ã®éã®å¿åé¢æ°ã®å¿ é ãã€ã³ãã«æ³šæããŠãã ããïŒïŒã管çè ãäœæããã«ã¯ã圌ã®åœ¹å²ãåå©çšããå¿ èŠããããŸãããã®ãããååã®åã«
admin_role
ã¢ã³ããŒã¹ã³ã¢ãä»ããŸãããåŸã§ãåæããŒã¿ãã¡ã€ã«ã§äœ¿çšãã
user_role
ã
admin_user
ãããã«äœ¿çšããããšãã§ããŸãããããã§ã¯ãã¢ã³ããŒã¹ã³ã¢ãåç §ããŠãã®ã³ãŒãããã®ãŸãŸã«ããŠãããŸããããã«ãããåæããŒã¿ãã¡ã€ã«ããããã§ãããã«èŠããŸããããã§ããã¹ãŠãåæããŒã¿ãããŒãããæºåãã§ããŸããã
$ mix run priv/repo/seeds.exs [debug] SELECT r0.âidâ, r0.ânameâ, r0.âadminâ, r0.âinserted_atâ, r0.âupdated_atâ FROM ârolesâ AS r0 WHERE ((r0.ânameâ = $1) AND (r0.âadminâ = $2)) [âUser Roleâ, false] OK query=81.7ms queue=2.8ms [debug] BEGIN [] OK query=0.2ms [debug] INSERT INTO ârolesâ (âadminâ, âinserted_atâ, ânameâ, âupdated_atâ) VALUES ($1, $2, $3, $4) RETURNING âidâ [false, {{2015, 11, 6}, {19, 35, 49, 0}}, âUser Roleâ, {{2015, 11, 6}, {19, 35, 49, 0}}] OK query=0.8ms [debug] COMMIT [] OK query=0.4ms [debug] SELECT r0.âidâ, r0.ânameâ, r0.âadminâ, r0.âinserted_atâ, r0.âupdated_atâ FROM ârolesâ AS r0 WHERE ((r0.ânameâ = $1) AND (r0.âadminâ = $2)) [âAdmin Roleâ, true] OK query=0.4ms [debug] BEGIN [] OK query=0.2ms [debug] INSERT INTO ârolesâ (âadminâ, âinserted_atâ, ânameâ, âupdated_atâ) VALUES ($1, $2, $3, $4) RETURNING âidâ [true, {{2015, 11, 6}, {19, 35, 49, 0}}, âAdmin Roleâ, {{2015, 11, 6}, {19, 35, 49, 0}}] OK query=0.4ms [debug] COMMIT [] OK query=0.3ms [debug] SELECT u0.âidâ, u0.âusernameâ, u0.âemailâ, u0.âpassword_digestâ, u0.ârole_idâ, u0.âinserted_atâ, u0.âupdated_atâ FROM âusersâ AS u0 WHERE ((u0.âusernameâ = $1) AND (u0.âemailâ = $2)) [âadminâ, âadmin@test.comâ] OK query=0.7ms [debug] BEGIN [] OK query=0.3ms [debug] INSERT INTO âusersâ (âemailâ, âinserted_atâ, âpassword_digestâ, ârole_idâ, âupdated_atâ, âusernameâ) VALUES ($1, $2, $3, $4, $5, $6) RETURNING âidâ [âadmin@test.comâ, {{2015, 11, 6}, {19, 35, 49, 0}}, â$2b$12$.MuPBUVe/7/9HSOsccJYUOAD5IKEB77Pgz2oTJ/UvTvWYwAGn/Liâ, 2, {{2015, 11, 6}, {19, 35, 49, 0}}, âadminâ] OK query=1.2ms [debug] COMMIT [] OK query=1.1ms
åããŠããããããŒããããšããã¶ã€ã³ã®ããã¯ã衚瀺ãããŸã
INSERT
ããããïŒ ãã¹ãŠãæ£åžžã«æ©èœããããšãå®å šã«ç¢ºèªããã«ã¯ãããããå床ããŠã³ããŒãããŠãæ¿å ¥æäœãçºçããŠããªãããšã確èªããŸãããã
$ mix run priv/repo/seeds.exs Role: User Role already exists, skipping [debug] SELECT r0.âidâ, r0.ânameâ, r0.âadminâ, r0.âinserted_atâ, r0.âupdated_atâ FROM ârolesâ AS r0 WHERE ((r0.ânameâ = $1) AND (r0.âadminâ = $2)) [âUser Roleâ, false] OK query=104.8ms queue=3.6ms Role: Admin Role already exists, skipping [debug] SELECT r0.âidâ, r0.ânameâ, r0.âadminâ, r0.âinserted_atâ, r0.âupdated_atâ FROM ârolesâ AS r0 WHERE ((r0.ânameâ = $1) AND (r0.âadminâ = $2)) [âAdmin Roleâ, true] OK query=0.6ms User: admin already exists, skipping [debug] SELECT u0.âidâ, u0.âusernameâ, u0.âemailâ, u0.âpassword_digestâ, u0.ârole_idâ, u0.âinserted_atâ, u0.âupdated_atâ FROM âusersâ AS u0 WHERE ((u0.âusernameâ = $1) AND (u0.âemailâ = $2)) [âadminâ, âadmin@test.comâ] OK query=0.8ms
ãããïŒãã¹ãŠã確å®ã«æ©èœããŸããããã«ãç¬èªã®äŸ¿å©ãªé¢æ°ãèšè¿°ããããšã§åŸãããåã³ãåãæ¶ãããšã¯ã§ããŸãã
Ecto
ã
ãã¹ãã§ã®ç®¡çè ã®éè€ã«é¢ãããšã©ãŒ
ããã§ãããæç¹ã§ãã¹ãããŒã¿ããŒã¹ããªã»ãããããšãããŠãŒã¶ãŒã¯æ¢ã«ååšããŸãããšãããšã©ãŒã衚瀺ãããŸãããããä¿®æ£ããç°¡åãªïŒãããŠäžæçãªïŒæ¹æ³ãææ¡ããŸãããã¡ã€ã«
test/support/test_helper.ex
ãéããé¢æ°ãå€æŽããŸã
create_user
ã
def create_user(role, %{email: email, username: username, password: password, password_confirmation: password_confirmation}) do if user = Repo.get_by(User, username: username) do Repo.delete(user) end role |> build_assoc(:users) |> User.changeset(%{email: email, username: username, password: password, password_confirmation: password_confirmation}) |> Repo.insert end
äœã«æ¥ãã®ïŒ
ããã§ããŠãŒã¶ãŒãæçš¿ã圹å²ã ãã§ãªããå®å šã«ã°ãªãŒã³ãªãã¹ããã§ããŸããããŠãŒã¶ãŒç»é²ããŠãŒã¶ãŒã®å€æŽãæçš¿ã«å®è¡å¯èœãªå¶éãèšããŸããããããŠã圌ãã¯ããã€ãã®äŸ¿å©ãªãã«ããŒé¢æ°ãè¿œå ããŸãããä»åŸã®æçš¿ã§ã¯ãããã°ãšã³ãžã³ã«æ°ããã¯ãŒã«ãªæ©èœãè¿œå ããããšã«æéãå²ããŸãïŒ
Wunschããã®çµè«
æ¯é±ç§ãã¡ã¯ã©ãã©ãå¢ããŠãããããã¯åã°ããããšã§ãïŒå人ãã³ãã¥ããã£ãžã®é¢å¿ã«æè¬ããèªä¿¡ãè¡šæããŸãããç§ãã¡ã¯ãããæ£åœåããé±ã«æ°åæ°ããèå³æ·±ãçŽ æãã¢ããããŒãããããšããŸãããããã£ãŠãElixirã«é¢ãããã·ã¢èªã®ãã¥ãŒã¹ã¬ã¿ãŒããŸã 賌èªããŠããªãå Žåã¯ãæéãç¡é§ã«ããªãã§ãã ãããä»ããç»é²ãããšææ¥ãæ°ããéå®èšäºãå±ããŸãïŒç¹ã«ããªãã®ããã«ãç§ãã¡ã¯æåéãäžæ©äžåããŠããŸãã
ãŸãããã€ãã»ããŒãã¹ã®æ°ããã«ãªã«ãªæ¬ãProgramming Elixirãã®è³åãšããŠã³ã³ãã¹ããéå¬ããŠããŸããåå ããŠãåã€ããšã¯ããã»ã©é£ãããããŸããïŒ
ãŸããããªããããã奜ããªããããã眮ããå人ã«èšäºã転éããããšãå¿ããªãã§ãã ããããŸãã¯ããªããç§ãã¡ã®æŽ»åã奜ããªããçµå±ã®ãšãããéåžžã«å€ãã®ãŠãŒã¶ãŒãè¿ éã«åéããã»ã©ãçŽ æµãªèšèªElixirã«é¢ããã»ãšãã©ã®è³ªåãã«ããŒãããµã€ãã®ãã«ããŒãžã§ã³ãè¿ éã«ç«ã¡äžããããšãã§ããŸãã
ã·ãªãŒãºã®ä»ã®èšäº
- ãšã³ããªãŒ
- ãã°ã€ã³
- 圹å²ãè¿œå
- ã³ã³ãããŒã©ãŒã§åœ¹å²ãåŠçããŸã
- ExMachinaãæ¥ç¶ããŸã
- ããŒã¯ããŠã³ã®ãµããŒã
- ã³ã¡ã³ããè¿œå
- ã³ã¡ã³ãã§çµäº
- ãã£ã³ãã«
- ãã£ãã«ãã¹ã
- ãããã«
ç 究ã®æåãç§ãã¡ãšäžç·ã«ïŒ