Skip to content
This repository was archived by the owner on Jun 11, 2023. It is now read-only.

Commit 7efbfc2

Browse files
committed
WIP
1 parent 4d252fa commit 7efbfc2

6 files changed

Lines changed: 515 additions & 44 deletions

File tree

lib/log/model/log_type/macros.ex

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,43 @@ defmodule Helix.Log.Model.LogType.Macros do
2828

2929
defenum LogEnum, @logs
3030

31-
# @spec exists?(term) ::
32-
# boolean
33-
def exists?(log) do
34-
Enum.any?(@logs, fn {valid_log, _} -> valid_log == log end)
35-
end
36-
37-
def new(type, data_params) do
31+
@spec exists?(atom) ::
32+
boolean
33+
def exists?(log_type),
34+
do: Enum.any?(@logs, fn {valid_type, _} -> valid_type == log_type end)
35+
36+
@spec new(type, map) ::
37+
struct
38+
@doc """
39+
Creates a new struct for the given log `type`.
40+
"""
41+
def new(type, data_params),
42+
do: dispatch(type, :new, data_params)
43+
44+
@spec parse(type, map) ::
45+
struct
46+
@doc """
47+
Attempts to parse the potentially unsafe input into a valid LogData.
48+
"""
49+
def parse(type, unsafe_data_params),
50+
do: dispatch(type, :parse, unsafe_data_params)
51+
52+
@spec dispatch(type, atom, term) ::
53+
term
54+
defp dispatch(type, method, param) when not is_list(param),
55+
do: dispatch(type, method, [param])
56+
defp dispatch(type, method, params) do
3857
type
3958
|> get_type_module()
40-
|> apply(:new, [data_params])
59+
|> apply(method, params)
4160
end
4261

4362
end
4463
end
4564

65+
@doc """
66+
Top-level macro used to define a LogType and its underlying LogData.
67+
"""
4668
defmacro log(name, enum_id, do: block) do
4769
module_name =
4870
__CALLER__.module
@@ -67,6 +89,9 @@ defmodule Helix.Log.Model.LogType.Macros do
6789
end
6890
end
6991

92+
@doc """
93+
Converts the module into a LogData struct.
94+
"""
7095
defmacro data_struct(keys) do
7196
quote do
7297

@@ -76,17 +101,48 @@ defmodule Helix.Log.Model.LogType.Macros do
76101
end
77102
end
78103

79-
defmacro new(args, do: block) do
104+
@doc """
105+
Creates a new LogData from the given `data` map.
106+
"""
107+
defmacro new(data, do: block) do
80108
quote do
81109

82110
@doc false
83-
def new(unquote(args)) do
111+
def new(unquote(data)) do
84112
unquote(block)
85113
end
86114

87115
end
88116
end
89117

118+
@doc """
119+
Attempts to parse the given unsafe input into a valid LogData.
120+
"""
121+
defmacro parse(data, do: block) do
122+
quote do
123+
124+
@spec parse(term) ::
125+
{:ok, data :: struct}
126+
| :error
127+
@doc false
128+
def parse(map = unquote(data)) when is_map(map) do
129+
try do
130+
{:ok, unquote(block)}
131+
rescue
132+
RuntimeError ->
133+
:error
134+
135+
KeyError ->
136+
:error
137+
end
138+
end
139+
140+
def parse(not_map) when not is_map(not_map),
141+
do: :error
142+
143+
end
144+
end
145+
90146
@doc """
91147
Generates the boilerplate for a n-field log type.
92148
@@ -116,22 +172,6 @@ defmodule Helix.Log.Model.LogType.Macros do
116172
defmacro gen3(p1, p2, p3),
117173
do: do_gen3(p1, p2, p3)
118174

119-
defmacro parse(args, do: block) do
120-
quote do
121-
122-
@doc false
123-
def parse(unquote(args)) do
124-
try do
125-
{:ok, unquote(block)}
126-
rescue
127-
RuntimeError ->
128-
:error
129-
end
130-
end
131-
132-
end
133-
end
134-
135175
def validate(field_type, field_value) when is_atom(field_type) do
136176
fun = Utils.concat_atom(:validate_, field_type)
137177

@@ -222,9 +262,9 @@ defmodule Helix.Log.Model.LogType.Macros do
222262
parse(unsafe) do
223263
%__MODULE__{
224264
unquote(f1) =>
225-
validate(unquote(v_f1), Map.get(unsafe, unquote(str_f1))),
265+
validate(unquote(v_f1), Map.fetch!(unsafe, unquote(str_f1))),
226266
unquote(f2) =>
227-
validate(unquote(v_f2), Map.get(unsafe, unquote(str_f2)))
267+
validate(unquote(v_f2), Map.fetch!(unsafe, unquote(str_f2)))
228268
}
229269
end
230270

@@ -249,11 +289,11 @@ defmodule Helix.Log.Model.LogType.Macros do
249289
parse(unsafe) do
250290
%__MODULE__{
251291
unquote(f1) =>
252-
validate(unquote(v_f1), Map.get(unsafe, unquote(str_f1))),
292+
validate(unquote(v_f1), Map.fetch!(unsafe, unquote(str_f1))),
253293
unquote(f2) =>
254-
validate(unquote(v_f2), Map.get(unsafe, unquote(str_f2))),
294+
validate(unquote(v_f2), Map.fetch!(unsafe, unquote(str_f2))),
255295
unquote(f3) =>
256-
validate(unquote(v_f3), Map.get(unsafe, unquote(str_f3)))
296+
validate(unquote(v_f3), Map.fetch!(unsafe, unquote(str_f3)))
257297
}
258298
end
259299

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import Helix.Websocket.Request
2+
3+
request Helix.Log.Websocket.Requests.Forge do
4+
@moduledoc """
5+
`LogForgeRequest` is called when the player wants to forge a log. The forge
6+
operation may either edit an existing log or create a new one.
7+
"""
8+
9+
alias Helix.Log.Model.Log
10+
alias Helix.Log.Model.LogType
11+
alias Helix.Log.Henforcer.Log.Forge, as: LogForgeHenforcer
12+
13+
def check_params(request, socket) do
14+
case request.unsafe["action"] do
15+
"create" ->
16+
check_params_create(request, socket)
17+
18+
"edit" ->
19+
check_params_edit(request, socket)
20+
21+
_ ->
22+
reply_error(request, "bad_action")
23+
end
24+
end
25+
26+
defp check_params_create(request, _socket) do
27+
with \
28+
{:ok, log_info} <-
29+
cast_log_info(request.unsafe["log_type"], request.unsafe["log_data"])
30+
do
31+
params = %{action: :create, log_info: log_info}
32+
33+
update_params(request, params, reply: true)
34+
else
35+
{:error, reason} ->
36+
reply_error(request, reason)
37+
end
38+
end
39+
40+
defp check_params_edit(request, _socket) do
41+
with \
42+
{:ok, log_id} <- Log.ID.cast(request.unsafe["log_id"]),
43+
{:ok, log_info} <-
44+
cast_log_info(request.unsafe["log_type"], request.unsafe["log_data"])
45+
do
46+
params = %{action: :edit, log_id: log_id, log_info: log_info}
47+
48+
update_params(request, params, reply: true)
49+
else
50+
{:error, reason} ->
51+
reply_error(request, reason)
52+
53+
_ ->
54+
bad_request(request)
55+
end
56+
end
57+
58+
def check_permissions(request = %{params: %{action: :create}}, socket) do
59+
gateway_id = socket.assigns.gateway.server_id
60+
61+
case LogForgeHenforcer.can_create?(gateway_id) do
62+
{true, relay} ->
63+
meta = %{gateway: relay.gateway, forger: relay.forger}
64+
update_meta(request, meta, reply: true)
65+
66+
{false, reason, _} ->
67+
reply_error(request, reason)
68+
end
69+
end
70+
71+
def check_permissions(request = %{params: %{action: :edit}}, socket) do
72+
log_id = request.params.log_id
73+
gateway_id = socket.assigns.gateway.server_id
74+
target_id = socket.assigns.destination.server_id
75+
76+
case LogForgeHenforcer.can_edit?(log_id, gateway_id, target_id) do
77+
{true, relay} ->
78+
meta = %{gateway: relay.gateway, forger: relay.forger, log: relay.log}
79+
update_meta(request, meta, reply: true)
80+
81+
{false, reason, _} ->
82+
reply_error(request, reason)
83+
end
84+
end
85+
86+
def handle_request(request, socket) do
87+
end
88+
89+
@spec cast_log_info(String.t, map) ::
90+
{:ok, Log.info}
91+
| {:error, :bad_log_type | :bad_log_data}
92+
defp cast_log_info(unsafe_log_type, unsafe_log_data) do
93+
with \
94+
{:ok, log_type} <- cast_existing_atom(unsafe_log_type),
95+
true <- LogType.exists?(log_type) || {:error, :log_type},
96+
{:ok, log_data} <- LogType.parse(log_type, unsafe_log_data)
97+
do
98+
{:ok, {log_type, log_data}}
99+
else
100+
{:error, :atom_not_found} ->
101+
{:error, :bad_log_type}
102+
103+
{:error, :log_type} ->
104+
{:error, :bad_log_type}
105+
106+
:error ->
107+
{:error, :bad_log_data}
108+
end
109+
end
110+
111+
render_empty()
112+
end

lib/server/websocket/channel/server.ex

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ channel Helix.Server.Websocket.Channel.Server do
1313

1414
alias Helix.Server.State.Websocket.Channel, as: ServerWebsocketChannelState
1515

16+
alias Helix.Log.Websocket.Requests.Forge, as: LogForgeRequest
17+
1618
alias Helix.Network.Websocket.Requests.Browse, as: BrowseRequest
1719

1820
alias Helix.Software.Websocket.Requests.Cracker.Bruteforce,
@@ -137,6 +139,38 @@ channel Helix.Server.Websocket.Channel.Server do
137139
"""
138140
topic "config.check", ConfigCheckRequest
139141

142+
@doc """
143+
Starts a LogForgeProcess. When forging, the player may want to edit an
144+
existing log, or create a brand new log.
145+
146+
Params (create):
147+
- *log_type: Type of the desired log revision.
148+
- *log_data: Data of the desired log revision.
149+
- *action: Explicitly set action to "create".
150+
151+
Params (edit):
152+
- *log_id: ID of the log that will be edited.
153+
- *log_type: Type of the desired log revision.
154+
- *log_data: Data of the desired log revision.
155+
- *action: Explicitly set action to "edit".
156+
157+
Errors:
158+
159+
Input validation:
160+
- "bad_action" - Action is neither "edit" or "create".
161+
- "bad_log_type" - The given `log_type` is not valid.
162+
- "bad_log_data" - The given `log_data` is not valid for the `log_type`.
163+
164+
Henforcer:
165+
- "forger_not_found" - Player does not have a valid LogForger file.
166+
- "log_not_found" (edit) - The given log ID was not found.
167+
- "log_not_belongs" (edit) - Attempting to edit a log that does not belong to
168+
the open channel.
169+
170+
- base errors
171+
"""
172+
topic "log.forge", LogForgeRequest
173+
140174
@doc """
141175
Updates the player's motherboard. May be used to attach, detach or update the
142176
mobo components.

lib/server/websocket/requests/motherboard_update.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ request Helix.Server.Websocket.Requests.MotherboardUpdate do
2020
end
2121
end
2222

23-
def check_detach(request, socket) do
23+
defp check_detach(request, socket) do
2424
with \
2525
true <- socket.assigns.meta.access == :local || :bad_src
2626
do
@@ -34,7 +34,7 @@ request Helix.Server.Websocket.Requests.MotherboardUpdate do
3434
end
3535
end
3636

37-
def check_update(request, socket) do
37+
defp check_update(request, socket) do
3838
with \
3939
true <- socket.assigns.meta.access == :local || :bad_src,
4040
{:ok, mobo_id} <- Component.ID.cast(request.unsafe["motherboard_id"]),

0 commit comments

Comments
 (0)