Skip to content

Commit e550e5a

Browse files
authored
Handle invalid POST to action resources. (#50)
1 parent 4ff3ab6 commit e550e5a

4 files changed

Lines changed: 121 additions & 82 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
## [0.12.3] - 2020-05-04
6+
### Changed
7+
- Invalid POST requests to action resources now generate an error status.
8+
59
## [0.12.2] - 2020-03-27
610
### Changed
711
- Updated dependencies.
@@ -40,7 +44,8 @@
4044
### Changed
4145
- Property, Action, and Event description now use `links` rather than `href`. - [Spec PR](https://github.com/mozilla-iot/wot/pull/119)
4246

43-
[Unreleased]: https://github.com/mozilla-iot/webthing-rust/compare/v0.12.2...HEAD
47+
[Unreleased]: https://github.com/mozilla-iot/webthing-rust/compare/v0.12.3...HEAD
48+
[0.12.3]: https://github.com/mozilla-iot/webthing-rust/compare/v0.12.2...v0.12.3
4449
[0.12.2]: https://github.com/mozilla-iot/webthing-rust/compare/v0.12.1...v0.12.2
4550
[0.12.1]: https://github.com/mozilla-iot/webthing-rust/compare/v0.12.0...v0.12.1
4651
[0.12.0]: https://github.com/mozilla-iot/webthing-rust/compare/v0.11.0...v0.12.0

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "webthing"
3-
version = "0.12.2"
3+
version = "0.12.3"
44
authors = ["Mozilla IoT <iot@mozilla.com>"]
55
repository = "https://github.com/mozilla-iot/webthing-rust"
66
homepage = "https://github.com/mozilla-iot/webthing-rust"
@@ -15,7 +15,7 @@ actix-web = { version = "0.7", default-features = false, features = ["brotli", "
1515
chrono = "0.4"
1616
get_if_addrs = "0.5"
1717
hostname = "0.3"
18-
libmdns = "0.2"
18+
libmdns = "0.4"
1919
openssl = { version = "0.10", optional = true }
2020
serde_json = "1.0"
2121
uuid = { version = "0.8", features = ["v4"] }

src/server.rs

Lines changed: 86 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -587,49 +587,56 @@ fn actions_handler_POST(
587587

588588
let message = message.as_object().unwrap();
589589

590-
let mut response: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
591-
for (action_name, action_params) in message.iter() {
592-
let input = action_params.get("input");
590+
let keys: Vec<&String> = message.keys().collect();
591+
if keys.len() != 1 {
592+
return HttpResponse::BadRequest().finish();
593+
}
593594

594-
let action = req.state().get_action_generator().generate(
595-
Arc::downgrade(&thing.clone()),
596-
action_name.to_string(),
597-
input,
598-
);
595+
let action_name = keys[0];
596+
let action_params = message.get(action_name).unwrap();
597+
let input = action_params.get("input");
599598

600-
if action.is_some() {
601-
let action = action.unwrap();
602-
let id = action.get_id();
603-
let action = Arc::new(RwLock::new(action));
599+
let action = req.state().get_action_generator().generate(
600+
Arc::downgrade(&thing.clone()),
601+
action_name.to_string(),
602+
input,
603+
);
604604

605-
{
606-
let mut thing = thing.write().unwrap();
607-
let result = thing.add_action(action.clone(), input);
605+
if action.is_some() {
606+
let action = action.unwrap();
607+
let id = action.get_id();
608+
let action = Arc::new(RwLock::new(action));
608609

609-
if result.is_err() {
610-
continue;
611-
}
612-
}
610+
{
611+
let mut thing = thing.write().unwrap();
612+
let result = thing.add_action(action.clone(), input);
613613

614-
response.insert(
615-
action_name.to_string(),
616-
action
617-
.read()
618-
.unwrap()
619-
.as_action_description()
620-
.get(action_name)
621-
.unwrap()
622-
.clone(),
623-
);
614+
if result.is_err() {
615+
return HttpResponse::BadRequest().finish();
616+
}
617+
}
624618

625-
thing
626-
.write()
619+
let mut response: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
620+
response.insert(
621+
action_name.to_string(),
622+
action
623+
.read()
627624
.unwrap()
628-
.start_action(action_name.to_string(), id);
629-
}
630-
}
625+
.as_action_description()
626+
.get(action_name)
627+
.unwrap()
628+
.clone(),
629+
);
630+
631+
thing
632+
.write()
633+
.unwrap()
634+
.start_action(action_name.to_string(), id);
631635

632-
HttpResponse::Created().json(response)
636+
HttpResponse::Created().json(response)
637+
} else {
638+
HttpResponse::BadRequest().finish()
639+
}
633640
}
634641

635642
/// Handle a GET request to /actions/<action_name>.
@@ -676,50 +683,59 @@ fn action_handler_POST(
676683

677684
let message = message.as_object().unwrap();
678685

679-
let mut response: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
680-
for (name, action_params) in message.iter() {
681-
if name != action_name {
682-
continue;
683-
}
686+
let keys: Vec<&String> = message.keys().collect();
687+
if keys.len() != 1 {
688+
return HttpResponse::BadRequest().finish();
689+
}
690+
691+
if keys[0] != action_name {
692+
return HttpResponse::BadRequest().finish();
693+
}
684694

685-
let input = action_params.get("input");
695+
let action_params = message.get(action_name).unwrap();
696+
let input = action_params.get("input");
686697

687-
let action = req.state().get_action_generator().generate(
688-
Arc::downgrade(&thing.clone()),
689-
name.to_string(),
690-
input,
691-
);
698+
let action = req.state().get_action_generator().generate(
699+
Arc::downgrade(&thing.clone()),
700+
action_name.to_string(),
701+
input,
702+
);
692703

693-
if action.is_some() {
694-
let action = action.unwrap();
695-
let id = action.get_id();
696-
let action = Arc::new(RwLock::new(action));
704+
if action.is_some() {
705+
let action = action.unwrap();
706+
let id = action.get_id();
707+
let action = Arc::new(RwLock::new(action));
697708

698-
{
699-
let mut thing = thing.write().unwrap();
700-
let result = thing.add_action(action.clone(), input);
709+
{
710+
let mut thing = thing.write().unwrap();
711+
let result = thing.add_action(action.clone(), input);
701712

702-
if result.is_err() {
703-
continue;
704-
}
713+
if result.is_err() {
714+
return HttpResponse::BadRequest().finish();
705715
}
716+
}
706717

707-
response.insert(
708-
name.to_string(),
709-
action
710-
.read()
711-
.unwrap()
712-
.as_action_description()
713-
.get(name)
714-
.unwrap()
715-
.clone(),
716-
);
718+
let mut response: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
719+
response.insert(
720+
action_name.to_string(),
721+
action
722+
.read()
723+
.unwrap()
724+
.as_action_description()
725+
.get(action_name)
726+
.unwrap()
727+
.clone(),
728+
);
717729

718-
thing.write().unwrap().start_action(name.to_string(), id);
719-
}
720-
}
730+
thing
731+
.write()
732+
.unwrap()
733+
.start_action(action_name.to_string(), id);
721734

722-
HttpResponse::Created().json(response)
735+
HttpResponse::Created().json(response)
736+
} else {
737+
HttpResponse::BadRequest().finish()
738+
}
723739
}
724740

725741
/// Handle a GET request to /actions/<action_name>/<action_id>.

src/thing.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -670,15 +670,13 @@ impl Thing for BaseThing {
670670
) -> Result<(), &str> {
671671
let action_name = action.read().unwrap().get_name();
672672

673-
{
674-
if !self.available_actions.contains_key(&action_name) {
675-
return Err("Action type not found");
676-
}
673+
if !self.available_actions.contains_key(&action_name) {
674+
return Err("Action type not found");
675+
}
677676

678-
let action_type = self.available_actions.get(&action_name).unwrap();
679-
if !action_type.validate_action_input(input) {
680-
return Err("Action input invalid");
681-
}
677+
let action_type = self.available_actions.get(&action_name).unwrap();
678+
if !action_type.validate_action_input(input) {
679+
return Err("Action input invalid");
682680
}
683681

684682
action
@@ -922,7 +920,27 @@ impl AvailableAction {
922920
fn validate_action_input(&self, input: Option<&serde_json::Value>) -> bool {
923921
let mut scope = json_schema::Scope::new();
924922
let validator = if self.metadata.contains_key("input") {
925-
let schema = self.metadata.get("input").unwrap();
923+
let mut schema = self
924+
.metadata
925+
.get("input")
926+
.unwrap()
927+
.as_object()
928+
.unwrap()
929+
.clone();
930+
if schema.contains_key("properties") {
931+
let properties = schema
932+
.get_mut("properties")
933+
.unwrap()
934+
.as_object_mut()
935+
.unwrap();
936+
for value in properties.values_mut() {
937+
let value = value.as_object_mut().unwrap();
938+
value.remove("@type");
939+
value.remove("unit");
940+
value.remove("title");
941+
}
942+
}
943+
926944
match scope.compile_and_return(json!(schema), true) {
927945
Ok(s) => Some(s),
928946
Err(_) => None,

0 commit comments

Comments
 (0)