diff --git a/lib/plug/conn.ex b/lib/plug/conn.ex index c4650355..319563f5 100644 --- a/lib/plug/conn.ex +++ b/lib/plug/conn.ex @@ -1677,7 +1677,8 @@ defmodule Plug.Conn do update_cookies(%{conn | resp_cookies: resp_cookies}, &Map.put(&1, key, value)) end - defp maybe_sign_or_encrypt_cookie(conn, key, value, opts) do + @doc false + def maybe_sign_or_encrypt_cookie(%Conn{} = conn, key, value, opts) do {sign?, opts} = Keyword.pop(opts, :sign, false) {encrypt?, opts} = Keyword.pop(opts, :encrypt, false) diff --git a/lib/plug/test.ex b/lib/plug/test.ex index 76778511..5f3c720b 100644 --- a/lib/plug/test.ex +++ b/lib/plug/test.ex @@ -228,11 +228,23 @@ defmodule Plug.Test do @doc """ Puts a request cookie. + + ## Options + + * `:max_age` - the cookie max-age, in seconds. Unset by default. + * `:sign` - when true, signs the cookie. + * `:encrypt` - when true, encrypts the cookie. + """ - @spec put_req_cookie(Conn.t(), binary, binary) :: Conn.t() - def put_req_cookie(conn, key, value) when is_binary(key) and is_binary(value) do + @spec put_req_cookie(Conn.t(), binary, any, keyword) :: Conn.t() + def put_req_cookie(%Conn{} = conn, key, value, opts \\ []) + when is_binary(key) and is_list(opts) do conn = delete_req_cookie(conn, key) - %{conn | req_headers: [{"cookie", "#{key}=#{value}"} | conn.req_headers]} + opts = Keyword.take(opts, [:max_age, :sign, :encrypt]) + + {to_send_value, _opts} = Conn.maybe_sign_or_encrypt_cookie(conn, key, value, opts) + + %{conn | req_headers: [{"cookie", "#{key}=#{to_send_value}"} | conn.req_headers]} end @doc """ diff --git a/test/plug/conn_test.exs b/test/plug/conn_test.exs index 65925125..4dfae684 100644 --- a/test/plug/conn_test.exs +++ b/test/plug/conn_test.exs @@ -1249,6 +1249,32 @@ defmodule Plug.ConnTest do assert get_cookies(new_conn)["foo"] == get_resp_cookies(conn)["foo"][:value] refute Map.has_key?(fetch_cookies(new_conn, encrypted: "foo").cookies, "foo") end + + test "put_req_cookie/4 with sign: true" do + conn = secret_conn() + assert get_req_header(conn, "cookie") == [] + + conn = put_req_cookie(conn, "foo", "signed-bar", sign: true) + assert [cookie] = get_req_header(conn, "cookie") + assert cookie != "foo=signed-bar" + assert cookie =~ ~r"foo=[\w\_\.]+" + + # indicates no options were set to the header, only the signed value + refute cookie =~ ~r/;/ + end + + test "put_req_cookie/4 with encrypt: true" do + conn = secret_conn() + assert get_req_header(conn, "cookie") == [] + + conn = put_req_cookie(conn, "foo", "encrypted-bar", encrypt: true) + assert [cookie] = get_req_header(conn, "cookie") + assert cookie != "foo=encrypted-bar" + assert cookie =~ ~r"foo=[\w\_\.]+" + + # indicates no options were set to the header, only the encrypted value + refute cookie =~ ~r/;/ + end end test "fetch_session/2 returns the same conn on subsequent calls" do