summaryrefslogtreecommitdiff
path: root/angelsharkd
diff options
context:
space:
mode:
Diffstat (limited to 'angelsharkd')
-rw-r--r--angelsharkd/Cargo.toml2
-rw-r--r--angelsharkd/README.md6
-rw-r--r--angelsharkd/src/routes/extensions/mod.rs6
-rw-r--r--angelsharkd/src/routes/extensions/simple_deprov.rs5
-rw-r--r--angelsharkd/src/routes/extensions/simple_deprov/README.md61
-rw-r--r--angelsharkd/src/routes/extensions/simple_deprov/mod.rs70
-rw-r--r--angelsharkd/src/routes/extensions/simple_search/mod.rs6
7 files changed, 142 insertions, 14 deletions
diff --git a/angelsharkd/Cargo.toml b/angelsharkd/Cargo.toml
index 9fdb00c..0bb1294 100644
--- a/angelsharkd/Cargo.toml
+++ b/angelsharkd/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "angelsharkd"
-version = "0.2.0"
+version = "0.2.1"
edition = "2021"
authors = ["Adam T. Carpenter <adam.carpenter@adp.com>"]
description = "A HTTP interface into one or more Communication Managers"
diff --git a/angelsharkd/README.md b/angelsharkd/README.md
index 8315c74..2287315 100644
--- a/angelsharkd/README.md
+++ b/angelsharkd/README.md
@@ -225,7 +225,11 @@ runtime.
## Login Configuration
-TODO:
+`angelsharkd` uses the same login configuration file syntax as `angelsharkcli`.
+See the
+[`angelsharkcli` README for more information](/angelsharkcli/README.md#login-configuration).
+
+You can download a [sample `asa.cfg.sample`](/asa.cfg.sample) to start with.
## Logging
diff --git a/angelsharkd/src/routes/extensions/mod.rs b/angelsharkd/src/routes/extensions/mod.rs
index cada47f..54053af 100644
--- a/angelsharkd/src/routes/extensions/mod.rs
+++ b/angelsharkd/src/routes/extensions/mod.rs
@@ -8,7 +8,7 @@ mod simple_search;
/// The extension filter; consists of all compiled optional Angelshark extension
/// filters combined under `/extensions`.
-pub fn filter(_config: &Config) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
+pub fn filter(config: &Config) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
// Note: this next line deals with the common edge case of having no
// extensions loaded with feature flags. It ensures that the the type
// checking is right when the return `.and()` is called below.
@@ -17,14 +17,14 @@ pub fn filter(_config: &Config) -> impl Filter<Extract = impl Reply, Error = Rej
// Block to enable simple_search extension feature. Instantiates a
// searchable haystack and configures filters to handle search requests.
#[cfg(feature = "simple_search")]
- let haystack = simple_search::Haystack::new(_config.runner.clone());
+ let haystack = simple_search::Haystack::new(config.runner.clone());
#[cfg(feature = "simple_search")]
let filters = filters
.or(simple_search::search_filter(haystack.clone()))
.or(simple_search::refresh_filter(haystack));
#[cfg(feature = "simple_deprov")]
- let filters = filters.or(simple_deprov::filter());
+ let filters = filters.or(simple_deprov::filter(config.runner.clone()));
path("extensions").and(filters)
}
diff --git a/angelsharkd/src/routes/extensions/simple_deprov.rs b/angelsharkd/src/routes/extensions/simple_deprov.rs
deleted file mode 100644
index 2b5ad40..0000000
--- a/angelsharkd/src/routes/extensions/simple_deprov.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-use warp::{Filter, Rejection, Reply};
-
-pub fn filter() -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
- warp::path("deprov").map(|| -> &str { todo!() }) // TODO:
-}
diff --git a/angelsharkd/src/routes/extensions/simple_deprov/README.md b/angelsharkd/src/routes/extensions/simple_deprov/README.md
new file mode 100644
index 0000000..4900075
--- /dev/null
+++ b/angelsharkd/src/routes/extensions/simple_deprov/README.md
@@ -0,0 +1,61 @@
+# Daemon Extension `simple_deprov`
+
+This extension implements excruciatingly simple extension de-provisioning. For
+example, if an agent was provisioned with a `station-user` and an
+`agent-loginid`, you can submit those extensions, their type, and the ACM they
+were provision on. They relevant commands to remove those objects will be
+executed in parallel, and any errors encountered will be returned.
+
+## Getting Started
+
+To enable this feature, compile `angelsharkd` with the `simple_deprov` flag:
+
+```sh
+cargo build --bin angelsharkd --features simple_deprov ...
+```
+
+## `POST /extensions/deprov` Remove Objects
+
+The request type is TODO:
+
+```json
+POST /extensions/deprov
+[
+ {
+ "station-user": {
+ "acm": "01",
+ "ext": "17571230000"
+ }
+ },
+ {
+ "agent-loginid": {
+ "acm": "01",
+ "ext": "17571240000"
+ }
+ }
+]
+```
+
+If all of the deprov commands were successful, the response is an empty array.
+
+```json
+200 OK
+[]
+```
+
+If there were errors running the relevant deprov commands (such as when an
+extension does not exist), they are included in the resulting array.
+
+```json
+200 OK
+[
+ "ACM lab: 1 00000000 309e Extension exists but assigned to a different object",
+ "ACM lab: 1 00000000 2ed5 Extension assigned as remote extension on the uniform-dialplan form",
+ "ACM lab: 1 00000000 2ed5 Extension assigned as remote extension on the uniform-dialplan form"
+]
+```
+
+## Logging
+
+The `deprov` endpoint always returns successfully. Any errors encountered during
+the command execution are logged as `ERROR`.
diff --git a/angelsharkd/src/routes/extensions/simple_deprov/mod.rs b/angelsharkd/src/routes/extensions/simple_deprov/mod.rs
new file mode 100644
index 0000000..fc734f3
--- /dev/null
+++ b/angelsharkd/src/routes/extensions/simple_deprov/mod.rs
@@ -0,0 +1,70 @@
+use libangelshark::{AcmRunner, Message, ParallelIterator};
+use log::error;
+use serde::Deserialize;
+use std::convert::Infallible;
+use warp::{
+ body::{content_length_limit, json},
+ post, reply, Filter, Rejection, Reply,
+};
+
+const SIXTEEN_K: u64 = 1024 * 16;
+
+/// Returns a warp filter to handle HTTP POSTs for deprovisioning stations, agents, etc.
+pub fn filter(runner: AcmRunner) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
+ warp::path("deprov")
+ .and(post())
+ .and(content_length_limit(SIXTEEN_K))
+ .and(json())
+ .and_then(move |entries| remove_entries(entries, runner.to_owned()))
+}
+
+/// Queues removal commands for [Entries] on an [AcmRunner]. Gathers any errors encountered and returns those.
+async fn remove_entries(entries: Entries, mut runner: AcmRunner) -> Result<impl Reply, Infallible> {
+ // Construct OSSI messages to carry out removals.
+ for entry in entries {
+ match entry {
+ Entry::StationUser { acm, ext } => {
+ runner.queue_input(&acm, &Message::new(&format!("clear amw all {}", ext)));
+ runner.queue_input(&acm, &Message::new(&format!("remove station {}", ext)));
+ }
+ Entry::AgentLoginId { acm, ext } => {
+ runner.queue_input(
+ &acm,
+ &Message::new(&format!("remove agent-loginID {}", ext)),
+ );
+ }
+ }
+ }
+
+ // Gather any errors encountered and format them for the client response.
+ let errors: Vec<String> = runner
+ .run_cached()
+ .map(|(acm, output)| match output {
+ Ok(messages) => messages
+ .into_iter()
+ .filter_map(|message| Some(format!("ACM {}: {}", acm.clone(), message.error?)))
+ .collect(),
+ Err(error) => vec![format!("ACM {}: {}", acm, error)],
+ })
+ .flatten()
+ .collect();
+
+ // Log errors for tracking.
+ for error in &errors {
+ error!("{}", error);
+ }
+
+ Ok(reply::json(&errors))
+}
+
+/// Collection of [Entry].
+type Entries = Vec<Entry>;
+
+/// Very basic [Deserialize] target for deprov inputs. Going from stringly typed to strongly typed.
+#[derive(Debug, Deserialize)]
+enum Entry {
+ #[serde(rename(deserialize = "station-user"))]
+ StationUser { acm: String, ext: String },
+ #[serde(rename(deserialize = "agent-loginid"))]
+ AgentLoginId { acm: String, ext: String },
+}
diff --git a/angelsharkd/src/routes/extensions/simple_search/mod.rs b/angelsharkd/src/routes/extensions/simple_search/mod.rs
index 5b5dc8e..159137a 100644
--- a/angelsharkd/src/routes/extensions/simple_search/mod.rs
+++ b/angelsharkd/src/routes/extensions/simple_search/mod.rs
@@ -21,7 +21,7 @@ pub fn search_filter(
.and(post())
.and(content_length_limit(1024 * 16))
.and(json())
- .and_then(move |terms: Needles| search(haystack.to_owned(), terms))
+ .and_then(move |needles: Needles| search(haystack.to_owned(), needles))
.with(with::header(header::PRAGMA, "no-cache"))
.with(with::header(header::CACHE_CONTROL, "no-store, max-age=0"))
.with(with::header(header::X_FRAME_OPTIONS, "DENY"))
@@ -39,8 +39,6 @@ pub fn refresh_filter(
/// Runs the search request to find all needles in the haystack and converts the
/// results into a reply.
async fn search(haystack: Haystack, needles: Needles) -> Result<impl Reply, Infallible> {
- // Ok(haystack.search(Vec::new())?)
- // if let Ok(matches = haystack.search(needle);
match haystack.search(needles) {
Ok(matches) => Ok(reply::with_status(reply::json(&matches), StatusCode::OK)),
Err(e) => Ok(reply::with_status(
@@ -56,7 +54,7 @@ async fn refresh(haystack: Haystack) -> Result<impl Reply, Infallible> {
// Run refresh as a background task and immediately return.
tokio::spawn(async move {
if let Err(e) = haystack.refresh() {
- error!("{}", e.to_string()); // TODO: use logger
+ error!("{}", e.to_string());
} else {
info!("Search haystack refreshed.");
}