diff --git a/doc/POABackend.Auth.REST.html b/doc/POABackend.Auth.REST.html index 803e8f1..2b45bd7 100644 --- a/doc/POABackend.Auth.REST.html +++ b/doc/POABackend.Auth.REST.html @@ -170,9 +170,9 @@ content-length: 362 cache-control: max-age=0, private, must-revalidate {"token":"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJwb2FfYmFja2VuZCIsImV4cCI6MTUzMzkzNjMwNiwiaWF0IjoxNTMzOTMyNzA2LCJpc3MiOiJwb2FfYmFja2VuZCIsImp0aSI6ImI0MzBkNTMwLWExZDYtNDk1Yy1hMjYyLThjNTcxMmM1OTM4YSIsIm5iZiI6MTUzMzkzMjcwNSwic3ViIjoiUmp1YURzdi0iLCJ0eXAiOiJhY2Nlc3MifQ.E3gqpCxY5wAAhZwcr7vZVLcC7X-bSHcXfX6NgeJc-LMbpcDgJvZgcgYQ-VTIkulb2mWw_Fjc7sXVwYMeIIliMg"} -

- - User Endpoint +

+ + Create User Endpoint

This Endpoint is needed in order to add a new user. Only Admin people can do that.

@@ -352,6 +352,83 @@ server: Cowboy date: Tue, 04 Sep 2018 13:49:45 GMT content-length: 0 cache-control: max-age=0, private, must-revalidate +

+ + Update User Endpoint +

+ +

This Endpoint is needed in order to update a user. Currently only the active property can be updated. If a user is set to active: false means +it was banned. We can use this enpoint in order to ban or unban users too.

+

PATCH /user/:username

+ ++++ + + + + + + + + + + + +
HTTP headerValues
content-typeapplication/json or application/msgpack
authorizationBasic encodeBase64(adminname + “:” + password)
+ ++++ + + + + + + + + + + + +
PayloadValue
JSON{“active” : boolean()}
MessagePackSame as JSON but packed with MessagePack
+

Response

+ ++++ + + + + + + + + + + + + + + + + + + + + +
CODEDescription
204Success
401Authentication failed
404The user doesn’t exist
415Unsupported Media Type (only application/json and application/msgpack allowed)
422Unprocessable entity (the active value is not a boolean)
+

Example:

+
curl -i -X PATCH -H "Authorization: Basic YWRtaW4xOnBhc3N3b3JkMTIzNDU2Nzg=" -H "Content-Type: application/json" -d '{"active":false}' https://localhost:4003/user/cZFxFfNT --insecure
+
+HTTP/1.1 204 No Content
+server: Cowboy
+date: Wed, 05 Sep 2018 13:38:32 GMT
+content-length: 0
+cache-control: max-age=0, private, must-revalidate

Blacklist Token Endpoint diff --git a/lib/poa_backend/auth/rest.ex b/lib/poa_backend/auth/rest.ex index ace47f0..8fb1f81 100644 --- a/lib/poa_backend/auth/rest.ex +++ b/lib/poa_backend/auth/rest.ex @@ -57,7 +57,7 @@ defmodule POABackend.Auth.REST do {"token":"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJwb2FfYmFja2VuZCIsImV4cCI6MTUzMzkzNjMwNiwiaWF0IjoxNTMzOTMyNzA2LCJpc3MiOiJwb2FfYmFja2VuZCIsImp0aSI6ImI0MzBkNTMwLWExZDYtNDk1Yy1hMjYyLThjNTcxMmM1OTM4YSIsIm5iZiI6MTUzMzkzMjcwNSwic3ViIjoiUmp1YURzdi0iLCJ0eXAiOiJhY2Nlc3MifQ.E3gqpCxY5wAAhZwcr7vZVLcC7X-bSHcXfX6NgeJc-LMbpcDgJvZgcgYQ-VTIkulb2mWw_Fjc7sXVwYMeIIliMg"} ``` - ## User Endpoint + ## Create User Endpoint This Endpoint is needed in order to add a new user. Only Admin people can do that. @@ -170,6 +170,45 @@ defmodule POABackend.Auth.REST do cache-control: max-age=0, private, must-revalidate ``` + ## Update User Endpoint + + This Endpoint is needed in order to update a user. Currently only the `active` property can be updated. If a user is set to `active: false` means + it was banned. We can use this enpoint in order to ban or unban users too. + + `PATCH /user/:username` + + HTTP header | Values + -- | -- + content-type | application/json or application/msgpack + authorization | Basic encodeBase64(adminname + “:” + password) + + Payload | Value + -- | -- + JSON | {"active" : boolean()} + MessagePack | Same as JSON but packed with MessagePack + + Response + + CODE | Description + -- | -- + 204 | Success + 401 | Authentication failed + 404 | The user doesn't exist + 415 | Unsupported Media Type (only application/json and application/msgpack allowed) + 422 | Unprocessable entity (the active value is not a boolean) + + Example: + + ``` + curl -i -X PATCH -H "Authorization: Basic YWRtaW4xOnBhc3N3b3JkMTIzNDU2Nzg=" -H "Content-Type: application/json" -d '{"active":false}' https://localhost:4003/user/cZFxFfNT --insecure + + HTTP/1.1 204 No Content + server: Cowboy + date: Wed, 05 Sep 2018 13:38:32 GMT + content-length: 0 + cache-control: max-age=0, private, must-revalidate + ``` + ## Blacklist Token Endpoint This Endpoint is used when we want to ban a single JWT Token (not the entire user) and that will convert that Token invalid. This Endpoint is only called by Admins. diff --git a/lib/poa_backend/auth/router.ex b/lib/poa_backend/auth/router.ex index 35800de..75d6d39 100644 --- a/lib/poa_backend/auth/router.ex +++ b/lib/poa_backend/auth/router.ex @@ -107,6 +107,31 @@ defmodule POABackend.Auth.Router do end end + patch "/user/:user_name" do + with {"authorization", "Basic " <> base64} <- List.keyfind(conn.req_headers, "authorization", 0), + {:ok, decoded64} <- Base.decode64(base64), + [admin_name, admin_password] <- String.split(decoded64, ":"), + {:ok, :valid} <- Auth.authenticate_admin(admin_name, admin_password), + true <- is_boolean(conn.params["active"]) + do + case Auth.get_user(user_name) do + nil -> send_resp(conn, 404, "") + user -> + set_active_user(user, conn.params["active"]) + send_resp(conn, 204, "") + end + else + false -> + conn + |> send_resp(422, "") + |> halt + _error -> + conn + |> send_resp(401, "") + |> halt + end + end + post "/blacklist/user" do with {"authorization", "Basic " <> base64} <- List.keyfind(conn.req_headers, "authorization", 0), {:ok, decoded64} <- Base.decode64(base64), @@ -163,4 +188,13 @@ defmodule POABackend.Auth.Router do send_resp(conn, 404, "") end + defp set_active_user(user, true) do + Auth.activate_user(user) + :ok + end + defp set_active_user(user, false) do + Auth.deactivate_user(user) + :ok + end + end \ No newline at end of file diff --git a/lib/poa_backend/custom_handler/rest/plugs/content_type.ex b/lib/poa_backend/custom_handler/rest/plugs/content_type.ex index 9d8e83c..0150f09 100644 --- a/lib/poa_backend/custom_handler/rest/plugs/content_type.ex +++ b/lib/poa_backend/custom_handler/rest/plugs/content_type.ex @@ -9,7 +9,7 @@ defmodule POABackend.CustomHandler.REST.Plugs.ContentType do accepted_content_type end - def call(%Conn{method: method} = conn, accepted_content_type) when method in ["POST", "PUT"] do + def call(%Conn{method: method} = conn, accepted_content_type) when method in ["POST", "PUT", "PATCH"] do import Plug.Conn with {"content-type", content_type} <- List.keyfind(conn.req_headers, "content-type", 0), diff --git a/test/auth/api_test.exs b/test/auth/api_test.exs index 99de1f4..6d4e68f 100644 --- a/test/auth/api_test.exs +++ b/test/auth/api_test.exs @@ -387,6 +387,198 @@ defmodule Auth.APITest do assert {401, :nobody} == delete(url <> "/ferigis", headers) end + test "updating user [JSON]" do + url = @base_url <> "/user" + mime_type = "application/json" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)} + ] + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + + result = + %{} + |> Map.put(:active, false) + |> Poison.encode! + |> patch(url <> "/ferigis", headers) + + assert {204, :nobody} == result + + {200, [initial_user]} = get(url, headers) + + refute initial_user["active"] + + result = + %{} + |> Map.put(:active, true) + |> Poison.encode! + |> patch(url <> "/ferigis", headers) + + assert {204, :nobody} == result + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + end + + test "updating user [MSGPACK]" do + url = @base_url <> "/user" + mime_type = "application/msgpack" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)} + ] + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + + result = + %{} + |> Map.put(:active, false) + |> Msgpax.pack! + |> patch(url <> "/ferigis", headers) + + assert {204, :nobody} == result + + {200, [initial_user]} = get(url, headers) + + refute initial_user["active"] + + result = + %{} + |> Map.put(:active, true) + |> Msgpax.pack! + |> patch(url <> "/ferigis", headers) + + assert {204, :nobody} == result + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + end + + test "updating user with wrong active value (not boolean) [JSON]" do + url = @base_url <> "/user" + mime_type = "application/json" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)} + ] + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + + result = + %{} + |> Map.put(:active, "wrong value") + |> Poison.encode! + |> patch(url <> "/ferigis", headers) + + assert {422, :nobody} == result + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + end + + test "updating user with wrong active value (not boolean) [MSGPACK]" do + url = @base_url <> "/user" + mime_type = "application/msgpack" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)} + ] + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + + result = + %{} + |> Map.put(:active, "wrong value") + |> Msgpax.pack! + |> patch(url <> "/ferigis", headers) + + assert {422, :nobody} == result + + {200, [initial_user]} = get(url, headers) + + assert initial_user["active"] + end + + test "updating user with wrong admin credentials [JSON]" do + url = @base_url <> "/user" + mime_type = "application/json" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> "wrongpassword")} + ] + + result = + %{} + |> Map.put(:active, false) + |> Poison.encode! + |> patch(url <> "/ferigis", headers) + + assert {401, :nobody} == result + end + + test "updating user with wrong admin credentials [MSGPACK]" do + url = @base_url <> "/user" + mime_type = "application/msgpack" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> "wrongpassword")} + ] + + result = + %{} + |> Map.put(:active, false) + |> Msgpax.pack! + |> patch(url <> "/ferigis", headers) + + assert {401, :nobody} == result + end + + test "updating user who doesn't exist [JSON]" do + url = @base_url <> "/user" + mime_type = "application/json" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)} + ] + + result = + %{} + |> Map.put(:active, true) + |> Poison.encode! + |> patch(url <> "/unnexistinguser", headers) + + assert {404, :nobody} == result + end + + test "updating user who doesn't exist [MSGPACK]" do + url = @base_url <> "/user" + mime_type = "application/msgpack" + headers = [ + {"Content-Type", mime_type}, + {"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)} + ] + + result = + %{} + |> Map.put(:active, true) + |> Msgpax.pack! + |> patch(url <> "/unnexistinguser", headers) + + assert {404, :nobody} == result + end + # ---------------------------------------- # /blacklist/user Endpoint Tests # ---------------------------------------- @@ -745,4 +937,18 @@ defmodule Auth.APITest do {response.status_code, body} end + defp patch(data, url, headers) do + options = [ssl: [{:versions, [:'tlsv1.2']}], recv_timeout: 500] + {:ok, response} = HTTPoison.patch(url, data, headers, options) + + body = case response.body do + "" -> + :nobody + _ -> + {:ok, body} = Poison.decode(response.body) + body + end + + {response.status_code, body} + end end