https_everywhere_lib_core/
update_channels.rs

1use openssl::{rsa::Rsa, pkey::Public};
2use serde_json::Value;
3use crate::strings::ERROR_SERDE_PARSE;
4
5struct StaticJsonStrings {
6    pub name: &'static str,
7    pub update_path_prefix: &'static str,
8    pub scope: &'static str,
9    pub replaces_default_rulesets: &'static str,
10    pub pem: &'static str,
11}
12
13const JSON_STRINGS: StaticJsonStrings = StaticJsonStrings {
14    name: "name",
15    update_path_prefix: "update_path_prefix",
16    scope: "scope",
17    replaces_default_rulesets: "replaces_default_rulesets",
18    pem: "pem",
19};
20
21
22/// An UpdateChannel defines where to find ruleset updates, the key to verify them, the scope they
23/// are applied to (which should be a regular expression), and whether they replace the default
24/// rulesets included with the application.
25#[derive(Debug)]
26pub struct UpdateChannel {
27    pub name: String,
28    pub key: Rsa<Public>,
29    pub update_path_prefix: String,
30    pub scope: Option<String>,
31    pub replaces_default_rulesets: bool,
32}
33
34impl From<&String> for UpdateChannel {
35    /// Returns an update channel given a JSON string
36    ///
37    /// # Arguments
38    ///
39    /// * `json_string` - A json string specifying the update channel.  See
40    /// [`tests/update_channels.json`](https://github.com/EFForg/https-everywhere-lib-core/blob/master/tests/update_channels.json) for the correct format
41    ///
42    /// # Panics
43    ///
44    /// Panics if a name, update path prefix, or pem is not specified, if the pem file does not
45    /// parse correctly into an RSA key, or it is not an object
46    fn from(json_string: &String) -> UpdateChannel {
47        let update_channel: Value = serde_json::from_str(&json_string).expect(ERROR_SERDE_PARSE);
48        UpdateChannel::from(&update_channel)
49    }
50}
51
52impl From<&Value> for UpdateChannel {
53    /// Returns an update channel given a serde_json::Value
54    ///
55    /// See the implementation of `From<&String>` for more detail
56    fn from(json_value: &Value) -> UpdateChannel {
57        if let Value::Object(update_channel) = json_value {
58            let name = match update_channel.get(JSON_STRINGS.name) {
59                Some(Value::String(name)) => name.to_string(),
60                _ => panic!("Name can not be blank")
61            };
62            let update_path_prefix = match update_channel.get(JSON_STRINGS.update_path_prefix) {
63                Some(Value::String(update_path_prefix)) => update_path_prefix.to_string(),
64                _ => panic!("Update path prefix can not be blank")
65            };
66            let scope = match update_channel.get(JSON_STRINGS.scope) {
67                Some(Value::String(scope)) if scope == "" => None,
68                Some(Value::String(scope)) => Some(scope.to_string()),
69                _ => None
70            };
71            let replaces_default_rulesets = match update_channel.get(JSON_STRINGS.replaces_default_rulesets) {
72                Some(Value::Bool(replaces_default_rulesets)) => replaces_default_rulesets.clone(),
73                _ => false
74            };
75            let key = match update_channel.get(JSON_STRINGS.pem) {
76                Some(Value::String(pem)) => {
77                    match Rsa::public_key_from_pem(&pem.clone().into_bytes()) {
78                        Ok(key) => key,
79                        _ => panic!("Could not parse public key")
80                    }
81                },
82                _ => panic!("Pem can not be blank")
83            };
84            UpdateChannel {
85                name,
86                key,
87                update_path_prefix,
88                scope,
89                replaces_default_rulesets,
90            }
91        } else {
92            panic!("Unexpected: update channel is not an object");
93        }
94    }
95}
96
97
98/// RuleSets consists of a tuple vec of update channels
99#[derive(Debug)]
100pub struct UpdateChannels(Vec<UpdateChannel>);
101
102impl UpdateChannels {
103    /// Get an immutable reference to all update channels
104    pub fn get_all(&self) -> &Vec<UpdateChannel>{
105       &self.0
106    }
107
108    /// Get a mutable reference to all update channels
109    pub fn get_all_mut(&mut self) -> &mut Vec<UpdateChannel>{
110       &mut self.0
111    }
112}
113
114/// Returns update channels given a JSON string
115///
116/// See the implementation of `From<&String> for UpdateChannel` for more detail
117///
118/// # Panics
119///
120/// Panics if the update channels JSON is not an array
121impl From<&String> for UpdateChannels {
122    fn from(json_string: &String) -> UpdateChannels {
123        if let Value::Array(update_channels) = serde_json::from_str(&json_string).expect(ERROR_SERDE_PARSE) {
124            UpdateChannels(update_channels.into_iter().map(|uc| {
125                UpdateChannel::from(&uc)
126            }).collect())
127        } else {
128            panic!("Unexpected: update channels is not an array")
129        }
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use std::fs;
137
138    fn mock_update_channels_json() -> String {
139        fs::read_to_string("tests/update_channels.json").unwrap()
140    }
141
142    fn create_mock_update_channels() -> UpdateChannels {
143        UpdateChannels::from(&mock_update_channels_json())
144    }
145
146    #[test]
147    fn creates_update_channels_correctly() {
148        let ucs = create_mock_update_channels();
149
150        let update_channels_representation = fs::read_to_string("tests/update_channels_representation.txt").unwrap();
151        assert_eq!(format!("{:?}", ucs), update_channels_representation);
152    }
153
154    #[test]
155    #[should_panic]
156    fn panics_if_no_name_specified() {
157        let mut update_channels: Value = serde_json::from_str(&mock_update_channels_json()).expect(ERROR_SERDE_PARSE);
158        update_channels.get_mut(0).unwrap().get_mut(JSON_STRINGS.name).unwrap().take();
159        UpdateChannel::from(update_channels.get(0).unwrap());
160    }
161
162    #[test]
163    #[should_panic]
164    fn panics_if_no_update_path_prefix_specified() {
165        let mut update_channels: Value = serde_json::from_str(&mock_update_channels_json()).expect(ERROR_SERDE_PARSE);
166        update_channels.get_mut(0).unwrap().get_mut(JSON_STRINGS.update_path_prefix).unwrap().take();
167        UpdateChannel::from(update_channels.get(0).unwrap());
168    }
169
170    #[test]
171    #[should_panic]
172    fn panics_if_no_pem_specified() {
173        let mut update_channels: Value = serde_json::from_str(&mock_update_channels_json()).expect(ERROR_SERDE_PARSE);
174        update_channels.get_mut(0).unwrap().get_mut(JSON_STRINGS.pem).unwrap().take();
175        UpdateChannel::from(update_channels.get(0).unwrap());
176    }
177
178    #[test]
179    #[should_panic]
180    fn panics_if_pem_specified_incorrectly() {
181        let mut update_channels: Value = serde_json::from_str(&mock_update_channels_json()).expect(ERROR_SERDE_PARSE);
182        let pem = update_channels.get_mut(0).unwrap().get_mut(JSON_STRINGS.pem).unwrap();
183        *pem = Value::String(String::from("Not a pem value"));
184        UpdateChannel::from(update_channels.get(0).unwrap());
185    }
186}