Skip to content

Commit 0cc304f

Browse files
committed
Migrate unregister_device queries to Ecto
Use Ecto to compose and run the query. This introduces a change in how the API responds to user requests for unregistering devices. Before this change, unregistering an existing device returned successfully regardless of the device registration status, and unregistering a non-existing device returned `{:error, :device_not_registered}` which was translated as a 403 Forbidden error from Pairing API. Now, the idempotency on existing devices is kept as before, but Pairing returns `{:error, :device_not_found}` for a non-existing device, which is translated by Pairing API as a 404 Not Found error. This change better reflects the error that actually happened instead of giving misleading feedback to API users. Moreover, for all concerns about authorization or security concerns, Pairing API already returns 403 Forbidden errors when the API user requests an operation on a device that is not allowed with the permissions contained in the auth token. This commit also ensures that `{:error, :device_not_found}` errors are correctly handled in a similar way by Pairing API in the case of other operations that return that error. Indeed, the APIs to get device credentials, verify device credentials, and get device info, may all return such an error, as the change was introduced in PR #1082. Signed-off-by: Davide Briani <davide.briani@secomind.com>
1 parent f0e495b commit 0cc304f

File tree

7 files changed

+39
-73
lines changed

7 files changed

+39
-73
lines changed

apps/astarte_pairing/lib/astarte_pairing/queries.ex

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,10 @@ defmodule Astarte.Pairing.Queries do
123123
end
124124

125125
def unregister_device(realm_name, device_id) do
126-
with :ok <- verify_already_registered_device(realm_name, device_id),
127-
:ok <- do_unregister_device(realm_name, device_id) do
126+
with {:ok, device} <- fetch_device(realm_name, device_id),
127+
{:ok, _device} <- do_unregister_device(realm_name, device) do
128128
:ok
129129
else
130-
%{acc: _acc, msg: msg} ->
131-
Logger.warning("DB error: #{inspect(msg)}")
132-
{:error, :database_error}
133-
134130
{:error, reason} ->
135131
Logger.warning("Unregister error: #{inspect(reason)}")
136132
{:error, reason}
@@ -147,44 +143,16 @@ defmodule Astarte.Pairing.Queries do
147143
end
148144
end
149145

150-
defp verify_already_registered_device(realm_name, device_id) do
151-
case fetch_device(realm_name, device_id) do
152-
{:ok, _device} -> :ok
153-
{:error, :device_not_found} -> {:error, :device_not_registered}
154-
{:error, reason} -> {:error, reason}
155-
end
156-
end
157-
158-
defp do_unregister_device(realm_name, device_id) do
159-
statement = """
160-
INSERT INTO devices
161-
(device_id, first_credentials_request, credentials_secret)
162-
VALUES (:device_id, :first_credentials_request, :credentials_secret)
163-
"""
164-
165-
query =
166-
Query.new()
167-
|> Query.statement(statement)
168-
|> Query.put(:device_id, device_id)
169-
|> Query.put(:first_credentials_request, nil)
170-
|> Query.put(:credentials_secret, nil)
171-
|> Query.consistency(:quorum)
172-
146+
defp do_unregister_device(realm_name, %Device{} = device) do
173147
keyspace_name =
174148
CQLUtils.realm_name_to_keyspace_name(realm_name, Config.astarte_instance_id!())
175149

176-
cqex_options =
177-
Config.cqex_options!()
178-
|> Keyword.put(:keyspace, keyspace_name)
179-
180-
with {:ok, client} <-
181-
CQEx.Client.new(
182-
Config.cassandra_node!(),
183-
cqex_options
184-
),
185-
{:ok, _res} <- Query.call(client, query) do
186-
:ok
187-
end
150+
device
151+
|> Ecto.Changeset.change(
152+
first_credentials_request: nil,
153+
credentials_secret: nil
154+
)
155+
|> Repo.update(prefix: keyspace_name, consistency: :quorum)
188156
end
189157

190158
def fetch_device(realm_name, device_id) do

apps/astarte_pairing/test/astarte_pairing/engine_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ defmodule Astarte.Pairing.EngineTest do
2020
use ExUnit.Case
2121

2222
alias Astarte.Core.Device
23-
alias Astarte.Pairing.Config
24-
alias Astarte.Core.CQLUtils
2523
alias Astarte.Pairing.CredentialsSecret
2624
alias Astarte.Pairing.DatabaseTestHelper
2725
alias Astarte.Pairing.Engine
@@ -226,7 +224,7 @@ defmodule Astarte.Pairing.EngineTest do
226224
test "fails with never registered device_id" do
227225
device_id = DatabaseTestHelper.unregistered_128_bit_hw_id()
228226

229-
assert {:error, :device_not_registered} = Engine.unregister_device(@test_realm, device_id)
227+
assert {:error, :device_not_found} = Engine.unregister_device(@test_realm, device_id)
230228
end
231229

232230
test "succeeds with registered and confirmed device_id, and makes it possible to register it again" do

apps/astarte_pairing_api/lib/astarte_pairing_api/credentials/credentials.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ defmodule Astarte.Pairing.API.Credentials do
4747
{:error, :forbidden} ->
4848
{:error, :forbidden}
4949

50+
{:error, :device_not_found} ->
51+
{:error, :device_not_found}
52+
5053
{:error, _other} ->
5154
{:error, :rpc_error}
5255
end
@@ -76,6 +79,9 @@ defmodule Astarte.Pairing.API.Credentials do
7679
{:error, :forbidden} ->
7780
{:error, :forbidden}
7881

82+
{:error, :device_not_found} ->
83+
{:error, :device_not_found}
84+
7985
{:error, _reason} ->
8086
{:error, :rpc_error}
8187
end

apps/astarte_pairing_api/lib/astarte_pairing_api/rpc/pairing.ex

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,6 @@ defmodule Astarte.Pairing.API.RPC.Pairing do
209209
end
210210

211211
defp extract_reply({:generic_error_reply, %GenericErrorReply{error_name: "device_not_found"}}) do
212-
{:error, :forbidden}
213-
end
214-
215-
defp extract_reply(
216-
{:generic_error_reply, %GenericErrorReply{error_name: "device_not_registered"}}
217-
) do
218212
{:error, :device_not_found}
219213
end
220214

apps/astarte_pairing_api/test/astarte_pairing_api/agent/agent_test.exs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,14 @@ defmodule Astarte.Pairing.API.AgentTest do
157157
reply: {:generic_ok_reply, %GenericOkReply{}}
158158
}
159159
|> Reply.encode()
160-
@encoded_device_not_registered_response %Reply{
161-
reply:
162-
{:generic_error_reply,
163-
%GenericErrorReply{
164-
error_name: "device_not_registered"
165-
}}
166-
}
167-
|> Reply.encode()
160+
@encoded_device_not_found_response %Reply{
161+
reply:
162+
{:generic_error_reply,
163+
%GenericErrorReply{
164+
error_name: "device_not_found"
165+
}}
166+
}
167+
|> Reply.encode()
168168
@encoded_realm_not_found_response %Reply{
169169
reply:
170170
{:generic_error_reply,
@@ -205,7 +205,7 @@ defmodule Astarte.Pairing.API.AgentTest do
205205
device_id: @test_device_id
206206
} = unregister_call
207207

208-
{:ok, @encoded_device_not_registered_response}
208+
{:ok, @encoded_device_not_found_response}
209209
end)
210210

211211
assert {:error, :device_not_found} = Agent.unregister_device(@test_realm, @test_device_id)

apps/astarte_pairing_api/test/astarte_pairing_api/credentials/credentials_test.exs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,13 @@ defmodule Astarte.Pairing.API.CredentialsTest do
189189
)
190190
end
191191

192-
test "returns forbidden with device not found" do
192+
test "returns device_not_found with device not found" do
193193
MockRPCClient
194194
|> expect(:rpc_call, fn _serialized_call, @rpc_destination, @timeout ->
195195
{:ok, @encoded_device_not_found_response}
196196
end)
197197

198-
assert {:error, :forbidden} =
198+
assert {:error, :device_not_found} =
199199
Credentials.get_astarte_mqtt_v1(
200200
@realm,
201201
@unexisting_hw_id,
@@ -336,13 +336,13 @@ defmodule Astarte.Pairing.API.CredentialsTest do
336336
)
337337
end
338338

339-
test "returns forbidden with device not found" do
339+
test "returns device_not_found with device not found" do
340340
MockRPCClient
341341
|> expect(:rpc_call, fn _serialized_call, @rpc_destination, @timeout ->
342342
{:ok, @encoded_device_not_found_response}
343343
end)
344344

345-
assert {:error, :forbidden} =
345+
assert {:error, :device_not_found} =
346346
Credentials.verify_astarte_mqtt_v1(
347347
@realm,
348348
@unexisting_hw_id,

apps/astarte_pairing_api/test/astarte_pairing_api_web/controllers/agent_controller_test.exs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,14 @@ defmodule Astarte.Pairing.APIWeb.AgentControllerTest do
212212
reply: {:generic_ok_reply, %GenericOkReply{}}
213213
}
214214
|> Reply.encode()
215-
@encoded_device_not_registered_response %Reply{
216-
reply:
217-
{:generic_error_reply,
218-
%GenericErrorReply{
219-
error_name: "device_not_registered"
220-
}}
221-
}
222-
|> Reply.encode()
215+
@encoded_device_not_found_response %Reply{
216+
reply:
217+
{:generic_error_reply,
218+
%GenericErrorReply{
219+
error_name: "device_not_found"
220+
}}
221+
}
222+
|> Reply.encode()
223223

224224
test "successful call", %{conn: conn} do
225225
MockRPCClient
@@ -234,13 +234,13 @@ defmodule Astarte.Pairing.APIWeb.AgentControllerTest do
234234
assert response(conn, 204) == ""
235235
end
236236

237-
test "renders errors when device is not registered", %{conn: conn} do
237+
test "renders errors when device does not exist", %{conn: conn} do
238238
MockRPCClient
239239
|> expect(:rpc_call, fn _serialized_call, @rpc_destination, @timeout ->
240240
{:ok, @encoded_pubkey_response}
241241
end)
242242
|> expect(:rpc_call, fn _serialized_call, @rpc_destination, @timeout ->
243-
{:ok, @encoded_device_not_registered_response}
243+
{:ok, @encoded_device_not_found_response}
244244
end)
245245

246246
conn = delete(conn, agent_path(conn, :delete, @realm, @device_id))

0 commit comments

Comments
 (0)