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

Enable Steps to be restarted #383

Merged
merged 3 commits into from
Feb 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions events.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"receives": ["All"],
"emits": [
"Story.Step.Proceeded",
"Story.Step.Failed",
"Story.Step.Restarted",
"Steppable (custom)"
]
},
Expand Down Expand Up @@ -157,9 +157,6 @@
],
"Software.Software.LogForger": [
"Process.Created"
],
"Story.Manager": [
"Story.Proceeded"
]
},
"process_conclusion": [
Expand All @@ -183,9 +180,13 @@
"Process.Created",
"Process.Completed",
"Process.Killed",
"File.Added",
"File.Deleted",
"File.Uploaded",
"StoryEmailSentEvent",
"StoryReplySentEvent",
"Story.Step.Restarted",
"Story.Step.Proceeded",
"Virus.Installed",
"Virus.InstallFailed"
],
Expand All @@ -196,9 +197,14 @@
"filters": ["Account.Created"],
"emits": []
},
"DownloadCrackerPublicFTP": {
"filters": ["File.Downloaded"],
"emits": []
"DownloadCracker": {
"filters": [
"File.Downloaded",
"File.Deleted"
],
"emits": [
"File.Added"
]
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/core/listener/model/owner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ defmodule Helix.Core.Listener.Model.Owner do
%__MODULE__{}
|> cast(params, @creation_fields)
|> validate_required(@required_fields)
|> unique_constraint(:owner_id_object_id_event_subscriber)
end

defmodule Query do
Expand Down
2 changes: 2 additions & 0 deletions lib/event/dispatcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ defmodule Helix.Event.Dispatcher do
event SoftwareEvent.Cracker.Bruteforce.Processed
event SoftwareEvent.Cracker.Overflow.Processed
event SoftwareEvent.File.Added
event SoftwareEvent.File.Deleted
event SoftwareEvent.File.Downloaded
event SoftwareEvent.File.DownloadFailed
event SoftwareEvent.File.Install.Processed
Expand Down Expand Up @@ -235,6 +236,7 @@ defmodule Helix.Event.Dispatcher do
event StoryEvent.Reply.Sent
event StoryEvent.Step.ActionRequested
event StoryEvent.Step.Proceeded
event StoryEvent.Step.Restarted

# Custom handlers
event StoryEvent.Reply.Sent,
Expand Down
5 changes: 3 additions & 2 deletions lib/hell/hack.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ defmodule HELL.Hack.Experience do
{:render, 3}
],
"Elixir.Helix.Story.Model.Steppable" => [
{:setup, 2},
{:start, 1},
{:setup, 1},
{:handle_event, 3},
{:complete, 1},
{:fail, 1},
{:restart, 3},
{:next_step, 1},
{:get_contact, 1},
{:format_meta, 1},
Expand Down
50 changes: 50 additions & 0 deletions lib/software/event/file.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,56 @@ defmodule Helix.Software.Event.File do
end
end

event Deleted do
@moduledoc """
FileDeletedEvent is fired when a file has been deleted on the filesystem.
Most of the times is called as a result of FileDeleteProcessedEvent
"""

alias Helix.Server.Model.Server
alias Helix.Software.Model.File

@type t ::
%__MODULE__{
file_id: File.id,
server_id: Server.id
}

event_struct [:file_id, :server_id]

@spec new(File.id, Server.id) ::
t
def new(file_id = %File.ID{}, server_id = %Server.ID{}) do
%__MODULE__{
file_id: file_id,
server_id: server_id
}
end

notify do
@moduledoc """
Pushes the notification to the Client, so it can remove the deleted file.
"""

@event :file_deleted

def generate_payload(event, _socket) do
data = %{
file_id: to_string(event.file_id)
}

{:ok, data}
end

def whom_to_notify(event),
do: %{server: [event.server_id]}
end

listenable(event) do
[event.file_id]
end
end

event Downloaded do
@moduledoc """
FileDownloadedEvent is fired when a FileTransfer process of type `download`
Expand Down
1 change: 1 addition & 0 deletions lib/software/event/handler/filesystem.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ defmodule Helix.Software.Event.Handler.Filesystem do
# Existing entries being updated

# Existing entries being removed
# TODO FileDeletedEvent #384

# Generic notifiers

Expand Down
19 changes: 13 additions & 6 deletions lib/software/make/file.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ defmodule Helix.Software.Make.File do
alias Helix.Software.Model.Software
alias Helix.Software.Model.Storage

alias Helix.Software.Event.File.Added, as: FileAddedEvent

@type modules :: cracker_modules

@type cracker_modules :: %{bruteforce: version, overflow: version}
Expand All @@ -16,31 +18,34 @@ defmodule Helix.Software.Make.File do
@typep file_parent :: Server.t | Storage.id
@typep version :: File.Module.version

@typep file_return(type) ::
{:ok, File.t_of_type(type), %{}, [FileAddedEvent.t]}

@doc """
Generates a cracker.
"""
@spec cracker(file_parent, cracker_modules, data) ::
{:ok, File.t_of_type(:cracker), %{}}
file_return(:cracker)
def cracker(parent, modules, data \\ %{}),
do: file(parent, :cracker, modules, data)

@spec cracker!(file_parent, cracker_modules, data) ::
File.t_of_type(:cracker)
def cracker!(parent, modules, data \\ %{}) do
{:ok, file, _} = cracker(parent, modules, data)
{:ok, file, _, _} = cracker(parent, modules, data)
file
end

@spec file(file_parent, Software.type, modules, data) ::
{:ok, File.t, %{}}
file_return(Software.type)
defp file(server = %Server{}, type, modules, data) do
server
|> CacheQuery.from_server_get_storages!()
|> List.first()
|> file(type, modules, data)
|> file(type, modules, data, server.server_id)
end

defp file(storage_id = %Storage.ID{}, type, modules, data) do
defp file(storage_id = %Storage.ID{}, type, modules, data, server_id) do
path = Map.get(data, :path, File.Default.path())

modules = format_modules(type, modules)
Expand All @@ -56,7 +61,9 @@ defmodule Helix.Software.Make.File do

{:ok, file} = FileAction.create(params, modules)

{:ok, file, %{}}
event = FileAddedEvent.new(file, server_id)

{:ok, file, %{}, [event]}
end

@spec format_modules(Software.type, modules) ::
Expand Down
8 changes: 4 additions & 4 deletions lib/software/make/pftp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ defmodule Helix.Software.Make.PFTP do
alias Helix.Software.Model.PublicFTP

@spec server(Server.t) ::
{:ok, PublicFTP.t, %{}}
{:ok, PublicFTP.t, %{}, []}
def server(server = %Server{}) do
{:ok, pftp} = PublicFTPFlow.enable_server(server)

{:ok, pftp, %{}}
{:ok, pftp, %{}, []}
end

@spec add_file(File.t, PublicFTP.t) ::
{:ok, PublicFTP.File.t, %{}}
{:ok, PublicFTP.File.t, %{}, []}
def add_file(file = %File{}, pftp = %PublicFTP{}) do
{:ok, pftp_file} = PublicFTPFlow.add_file(pftp, file)

{:ok, pftp_file, %{}}
{:ok, pftp_file, %{}, []}
end
end
8 changes: 8 additions & 0 deletions lib/software/model/public_ftp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ defmodule Helix.Software.Model.PublicFTP do
def disable_server(changeset = %Changeset{}),
do: put_change(changeset, :is_active, false)

@spec is_active?(t) ::
boolean
@doc """
Verifies whether the given server is active (enabled) or not.
"""
def is_active?(%__MODULE__{is_active: is_active?}),
do: is_active?

@spec create_changeset(creation_params) ::
changeset
defp create_changeset(params) do
Expand Down
12 changes: 8 additions & 4 deletions lib/software/query/public_ftp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Helix.Software.Query.PublicFTP do
alias Helix.Software.Model.File
alias Helix.Software.Model.PublicFTP

@spec fetch_file(File.t) ::
@spec fetch_file(File.idt) ::
PublicFTP.File.t
| nil
@doc """
Expand All @@ -15,9 +15,11 @@ defmodule Helix.Software.Query.PublicFTP do
- The PublicFTP server is active.
"""
def fetch_file(file = %File{}),
do: PublicFTPInternal.fetch_file(file.file_id)
do: fetch_file(file.file_id)
def fetch_file(file_id = %File.ID{}),
do: PublicFTPInternal.fetch_file(file_id)

@spec fetch_server(Server.t) ::
@spec fetch_server(Server.idt) ::
PublicFTP.t
| nil
@doc """
Expand All @@ -26,7 +28,9 @@ defmodule Helix.Software.Query.PublicFTP do
Disabled/inactive servers are returned as well.
"""
def fetch_server(server = %Server{}),
do: PublicFTPInternal.fetch(server.server_id)
do: fetch_server(server.server_id)
def fetch_server(server_id = %Server.ID{}),
do: PublicFTPInternal.fetch(server_id)

@spec list_files(Server.idt) ::
[File.t]
Expand Down
2 changes: 1 addition & 1 deletion lib/story/action/flow/story.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ defmodule Helix.Story.Action.Flow.Story do
with \
{:ok, _} <- ContextFlow.setup(entity),
{:ok, story_step} <- StoryAction.proceed_step(first_step),
{:ok, _, events} <- Steppable.setup(first_step, nil),
{:ok, _, events} <- Steppable.start(first_step),
on_success(fn -> Event.emit(events, from: relay) end)
do
{:ok, story_step}
Expand Down
44 changes: 44 additions & 0 deletions lib/story/action/story.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule Helix.Story.Action.Story do
alias Helix.Story.Event.Email.Sent, as: EmailSentEvent
alias Helix.Story.Event.Reply.Sent, as: ReplySentEvent
alias Helix.Story.Event.Step.Proceeded, as: StepProceededEvent
alias Helix.Story.Event.Step.Restarted, as: StepRestartedEvent

@spec proceed_step(first_step :: Step.t) ::
{:ok, Story.Step.t}
Expand Down Expand Up @@ -64,6 +65,15 @@ defmodule Helix.Story.Action.Story do
def notify_step(prev_step, next_step),
do: [StepProceededEvent.new(prev_step, next_step)]

@spec notify_restart(Step.t, atom, Step.email_id, Step.email_meta) ::
[StepRestartedEvent.t]
@doc """
Generates the StepRestartedEvent, used to notify the client that the step has
been restarted
"""
def notify_restart(step, reason, checkpoint, meta),
do: [StepRestartedEvent.new(step, reason, checkpoint, meta)]

@spec send_email(Step.t, Step.email_id, Step.email_meta) ::
{:ok, [EmailSentEvent.t]}
| {:error, :internal}
Expand Down Expand Up @@ -122,4 +132,38 @@ defmodule Helix.Story.Action.Story do
end
end)
end

@spec rollback_emails(Step.t, Step.email_id, Step.email_meta) ::
{:ok, Story.Step.t, Story.Email.t}
| {:error, :internal}
@doc """
Rollbacks the messages on `step` to the specified `checkpoint`.
Note that within the Story domain, messages are saved on two places:
- within Story.Step, used for internal step stuff (handling replies, etc)
- within Story.Email, used for listing messages per contact (with metadata)
As such, we need to update (rollback) to the checkpoint on both places.
The `allowed_replies` list, specified at `Story.Step.t`, will also be updated
with the default (unlocked) replies listed on `checkpoint` declaration.
"""
def rollback_emails(step, checkpoint, meta) do
result =
Repo.transaction fn ->
with \
{:ok, story_step} <- StepInternal.rollback_email(step, checkpoint),
{:ok, email} <- EmailInternal.rollback_email(step, checkpoint, meta)
do
{story_step, email}
else
_ ->
Repo.rollback(:internal)
end
end

with {:ok, {story_step, story_email}} <- result do
{:ok, story_step, story_email}
end
end
end
Loading