From afd1408c160e1db368cd12b354ad731452edfc77 Mon Sep 17 00:00:00 2001 From: brizental Date: Fri, 17 Jul 2020 17:35:07 +0200 Subject: [PATCH] Attend to review comments * Fix RFC link in `validate_base64url_encoding`; * Validate each field in JWE separately and specifically; * Fix comment for `set` function; * Add tests for get_test_value_* functions; * Apply lints from new Rust version. --- glean-core/ffi/src/jwe.rs | 9 +- glean-core/src/metrics/jwe.rs | 324 ++++++++++++++++++++++------------ glean-core/tests/jwe.rs | 62 ++++++- 3 files changed, 266 insertions(+), 129 deletions(-) diff --git a/glean-core/ffi/src/jwe.rs b/glean-core/ffi/src/jwe.rs index d316efebbc..b4c93ed7c2 100644 --- a/glean-core/ffi/src/jwe.rs +++ b/glean-core/ffi/src/jwe.rs @@ -60,15 +60,10 @@ pub extern "C" fn glean_jwe_test_has_value(metric_id: u64, storage_name: FfiStr) } #[no_mangle] -pub extern "C" fn glean_jwe_test_get_value( - metric_id: u64, - storage_name: FfiStr, -) -> *mut c_char { +pub extern "C" fn glean_jwe_test_get_value(metric_id: u64, storage_name: FfiStr) -> *mut c_char { with_glean_value(|glean| { JWE_METRICS.call_infallible(metric_id, |metric| { - metric - .test_get_value(glean, storage_name.as_str()) - .unwrap() + metric.test_get_value(glean, storage_name.as_str()).unwrap() }) }) } diff --git a/glean-core/src/metrics/jwe.rs b/glean-core/src/metrics/jwe.rs index 992f512a5c..6568827e8f 100644 --- a/glean-core/src/metrics/jwe.rs +++ b/glean-core/src/metrics/jwe.rs @@ -13,13 +13,13 @@ use crate::storage::StorageManager; use crate::CommonMetricData; use crate::Glean; -const DEFAULT_MAX_CHARS_PER_ELEMENT: usize = 1024; +const DEFAULT_MAX_CHARS_PER_VARIABLE_SIZE_ELEMENT: usize = 1024; /// Verifies if a string is [`BASE64URL`](https://tools.ietf.org/html/rfc4648#section-5) compliant. /// /// As such, the string must match the regex: `[a-zA-Z0-9\-\_]*`. /// -/// > **Note** As described in the [JWS specification](https://tools.ietf.org/html/rfc7516#section-2), +/// > **Note** As described in the [JWS specification](https://tools.ietf.org/html/rfc7515#section-2), /// > the BASE64URL encoding used by JWE discards any padding, /// > that is why we can ignore that for this validation. /// @@ -41,67 +41,56 @@ fn validate_base64url_encoding(value: &str) -> bool { } /// Representation of a [JWE](https://tools.ietf.org/html/rfc7516). +/// +/// **Note** Variable sized elements will be constrained to a length of DEFAULT_MAX_CHARS_PER_VARIABLE_SIZE_ELEMENT, +/// this is a constraint introduced by Glean and not part of the spec. #[derive(Serialize)] struct Jwe { + /// A variable-size JWE protected header. header: String, + /// A variable-size [encrypted key](https://tools.ietf.org/html/rfc7516#appendix-A.1.3). + /// This can be an empty octet sequence. key: String, + /// A fixed-size, 96-bit, base64 encoded [JWE Initialization vector](https://tools.ietf.org/html/rfc7516#appendix-A.1.4) (e.g. “48V1_ALb6US04U3b”). + /// If not required by the encryption algorithm, can be an empty octet sequence. init_vector: String, + /// The variable-size base64 encoded cipher text. cipher_text: String, + /// A fixed-size, 132-bit, base64 encoded authentication tag. + /// Can be an empty octet sequence. auth_tag: String, } impl Jwe { - /// Create a new JWE and validate all elements. - /// - /// Validation includes checking if each element is valid BASE64URL according the the JWE specification - /// and also checking if each element does not exceed DEFAULT_MAX_CHARS_PER_ELEMENT. - /// - /// **Note** The character limit is our own constraint, not part of the spec. - /// - /// ## Arguments - /// - /// * `header` - the JWE Protected Header element. - /// * `key` - the JWE Encrypted Key element. May be empy. - /// * `init_vector` - the JWE Initialization Vector element. May be empy. - /// * `cipher_text` - the JWE Ciphertext element. - /// * `auth_tag` - the JWE Authentication Tag element. May be empy. + /// Create a new JWE struct. fn new>( header: S, key: S, init_vector: S, cipher_text: S, auth_tag: S, - ) -> Result { - let header = header.into(); - let key = key.into(); - let init_vector = init_vector.into(); - let cipher_text = cipher_text.into(); - let auth_tag = auth_tag.into(); - - for element in [&header, &cipher_text].iter() { - if element.is_empty() { - return Err(( - ErrorType::InvalidValue, - "Elements `header` and `cipher_text` must not be empty.", - )); - } - } + ) -> Result { + let mut header = header.into(); + header = Self::validate_non_empty("header", header)?; + header = Self::validate_max_size("header", header)?; + header = Self::validate_base64url_encoding("header", header)?; - for element in [&header, &key, &init_vector, &cipher_text, &auth_tag].iter() { - if element.len() > DEFAULT_MAX_CHARS_PER_ELEMENT { - return Err(( - ErrorType::InvalidOverflow, - "Element in JWE value exceeds maximum number of characters.", - )); - } - - if !validate_base64url_encoding(&element) { - return Err(( - ErrorType::InvalidValue, - "Element in JWE value is not valid BASE64URL.", - )); - } - } + let mut key = key.into(); + key = Self::validate_max_size("key", key)?; + key = Self::validate_base64url_encoding("key", key)?; + + let mut init_vector = init_vector.into(); + init_vector = Self::validate_fixed_size_or_empty("init_vector", init_vector, 96)?; + init_vector = Self::validate_base64url_encoding("init_vector", init_vector)?; + + let mut cipher_text = cipher_text.into(); + cipher_text = Self::validate_non_empty("cipher_text", cipher_text)?; + cipher_text = Self::validate_max_size("cipher_text", cipher_text)?; + cipher_text = Self::validate_base64url_encoding("cipher_text", cipher_text)?; + + let mut auth_tag = auth_tag.into(); + auth_tag = Self::validate_fixed_size_or_empty("auth_tag", auth_tag, 128)?; + auth_tag = Self::validate_base64url_encoding("auth_tag", auth_tag)?; Ok(Self { header, @@ -111,11 +100,73 @@ impl Jwe { auth_tag, }) } + + fn validate_base64url_encoding( + name: &str, + value: String, + ) -> Result { + if !validate_base64url_encoding(&value) { + return Err(( + ErrorType::InvalidValue, + format!("`{}` element in JWE value is not valid BASE64URL.", name), + )); + } + + Ok(value) + } + + fn validate_non_empty(name: &str, value: String) -> Result { + if value.is_empty() { + return Err(( + ErrorType::InvalidValue, + format!("`{}` element in JWE value must not be empty.", name), + )); + } + + Ok(value) + } + + fn validate_max_size(name: &str, value: String) -> Result { + if value.len() > DEFAULT_MAX_CHARS_PER_VARIABLE_SIZE_ELEMENT { + return Err(( + ErrorType::InvalidOverflow, + format!( + "`{}` element in JWE value must not exceed {} characters.", + name, DEFAULT_MAX_CHARS_PER_VARIABLE_SIZE_ELEMENT + ), + )); + } + + Ok(value) + } + + fn validate_fixed_size_or_empty( + name: &str, + value: String, + size_in_bits: usize, + ) -> Result { + // Each Base64 digit represents exactly 6 bits of data. + // By dividing the size_in_bits by 6 and ceiling the result, + // we get the amount of characters the value should have. + let num_chars = (size_in_bits as f32 / 6f32).ceil() as usize; + if !value.is_empty() && value.len() != num_chars { + return Err(( + ErrorType::InvalidOverflow, + format!( + "`{}` element in JWE value must have exactly {}-bits or be empty.", + name, size_in_bits + ), + )); + } + + Ok(value) + } } -/// Trait implementation to convert a JWE [`compact representation`](https://tools.ietf.org/html/rfc7516#appendix-A.2.7) string into a Jwe struct. +/// Trait implementation to convert a JWE [`compact representation`](https://tools.ietf.org/html/rfc7516#appendix-A.2.7) +/// string into a Jwe struct. impl FromStr for Jwe { - type Err = (ErrorType, &'static str); + type Err = (ErrorType, String); fn from_str(s: &str) -> Result { let mut elements: Vec<&str> = s.split('.').collect(); @@ -123,7 +174,7 @@ impl FromStr for Jwe { if elements.len() != 5 { return Err(( ErrorType::InvalidValue, - "JWE value is not formatted as expected.", + "JWE value is not formatted as expected.".into(), )); } @@ -198,7 +249,7 @@ impl JweMetric { }; } - /// Build a JWE value from it's elements and set to it. + /// Build a JWE value from its elements and set to it. /// /// ## Arguments /// @@ -268,91 +319,134 @@ impl JweMetric { mod test { use super::*; - const HEADER: &str = "eyJhbGciOiJFQ0RILUVTIiwia2lkIjoiMFZFRTdmT0txbFdHVGZrY0taRUJ2WWl3dkpMYTRUUGlJVGxXMGJOcDdqVSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InY3Q1FlRWtVQjMwUGwxV0tPMUZUZ25OQlNQdlFyNlh0UnZxT2kzSWdzNHciLCJ5IjoiNDBKVEpaQlMwOXpWNHpxb0hHZDI5NGFDeHRqcGU5a09reGhELVctUEZsSSJ9LCJlbmMiOiJBMjU2R0NNIn0"; - const KEY: &str = ""; - const INIT_VECTOR: &str = "A_wzJya943vlHKFH"; - const CIPHER_TEXT: &str = "yq0JhkGZiZd6UiZK6goTcEf6i4gbbBeXxvq8QV5_nC4"; - const AUTH_TAG: &str = "Knl_sYSBrrP-aa54z6B6gA"; - const JWE: &str = "eyJhbGciOiJFQ0RILUVTIiwia2lkIjoiMFZFRTdmT0txbFdHVGZrY0taRUJ2WWl3dkpMYTRUUGlJVGxXMGJOcDdqVSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InY3Q1FlRWtVQjMwUGwxV0tPMUZUZ25OQlNQdlFyNlh0UnZxT2kzSWdzNHciLCJ5IjoiNDBKVEpaQlMwOXpWNHpxb0hHZDI5NGFDeHRqcGU5a09reGhELVctUEZsSSJ9LCJlbmMiOiJBMjU2R0NNIn0..A_wzJya943vlHKFH.yq0JhkGZiZd6UiZK6goTcEf6i4gbbBeXxvq8QV5_nC4.Knl_sYSBrrP-aa54z6B6gA"; + const HEADER: &str = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ"; + const KEY: &str = "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg"; + const INIT_VECTOR: &str = "48V1_ALb6US04U3b"; + const CIPHER_TEXT: &str = + "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A"; + const AUTH_TAG: &str = "XFBoMYUZodetZdvTiFvSkQ"; + const JWE: &str = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ"; #[test] - fn generates_jwe_from_compact_repr() { - // Errors if cipher_text and auth_tag are empty - if let Err((error_type, _)) = - Jwe::from_str(&format!(".{}.{}..{}", KEY, INIT_VECTOR, AUTH_TAG)) - { - assert_eq!(error_type, ErrorType::InvalidValue); - } else { - panic!("Should not have built JWE successfully.") - } + fn generates_jwe_from_correct_input() { + let jwe = Jwe::from_str(JWE).unwrap(); + assert_eq!(jwe.header, HEADER); + assert_eq!(jwe.key, KEY); + assert_eq!(jwe.init_vector, INIT_VECTOR); + assert_eq!(jwe.cipher_text, CIPHER_TEXT); + assert_eq!(jwe.auth_tag, AUTH_TAG); + + assert!(Jwe::new(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG).is_ok()); + } - // Errors if one of the parts is not valid BASE64URL - let invalid = "inv@alid value"; - if let Err((error_type, _)) = - Jwe::from_str(&format!("{}...{}.{}", HEADER, CIPHER_TEXT, invalid)) - { - assert_eq!(error_type, ErrorType::InvalidValue); - } else { - panic!("Should not have built JWE successfully.") + #[test] + fn jwe_validates_header_value_correctly() { + // When header is empty, correct error is returned + match Jwe::new("", KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidValue), } - // Errors if one of the parts exceeds max length + // When header is bigger than max size, correct error is returned let too_long = (0..1025).map(|_| "X").collect::(); - if let Err((error_type, _)) = - Jwe::from_str(&format!("{}...{}.{}", HEADER, CIPHER_TEXT, too_long)) - { - assert_eq!(error_type, ErrorType::InvalidOverflow); - } else { - panic!("Should not have built JWE successfully.") + match Jwe::new( + too_long, + KEY.into(), + INIT_VECTOR.into(), + CIPHER_TEXT.into(), + AUTH_TAG.into(), + ) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidOverflow), } - // Doesn't error if allowed elements are empty and puts the elements in the right places - let jwe = Jwe::from_str(&format!("{}...{}.", HEADER, CIPHER_TEXT)).unwrap(); - assert_eq!(jwe.header, HEADER); - assert_eq!(jwe.key, ""); - assert_eq!(jwe.init_vector, ""); - assert_eq!(jwe.cipher_text, CIPHER_TEXT); - assert_eq!(jwe.auth_tag, ""); + // When header is not valid BASE64URL, correct error is returned + let not64 = "inv@alid value!"; + match Jwe::new(not64, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidValue), + } + } - // Doesn't error for the sample value - let jwe = Jwe::from_str(JWE).unwrap(); - assert_eq!(jwe.header, HEADER); - assert_eq!(jwe.key, KEY); - assert_eq!(jwe.init_vector, INIT_VECTOR); - assert_eq!(jwe.cipher_text, CIPHER_TEXT); - assert_eq!(jwe.auth_tag, AUTH_TAG); + #[test] + fn jwe_validates_key_value_correctly() { + // When key is empty,JWE is created + assert!(Jwe::new(HEADER, "", INIT_VECTOR, CIPHER_TEXT, AUTH_TAG).is_ok()); + + // When key is bigger than max size, correct error is returned + let too_long = (0..1025).map(|_| "X").collect::(); + match Jwe::new(HEADER, &too_long, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidOverflow), + } + + // When key is not valid BASE64URL, correct error is returned + let not64 = "inv@alid value!"; + match Jwe::new(HEADER, not64, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidValue), + } } #[test] - fn generates_jwe_from_elements() { - // Errors if cipher_text and auth_tag are empty - if let Err((error_type, _)) = Jwe::new("", KEY, INIT_VECTOR, "", AUTH_TAG) { - assert_eq!(error_type, ErrorType::InvalidValue); - } else { - panic!("Should not have built JWE successfully.") + fn jwe_validates_init_vector_value_correctly() { + // When init_vector is empty, JWE is created + assert!(Jwe::new(HEADER, KEY, "", CIPHER_TEXT, AUTH_TAG).is_ok()); + + // When init_vector is not the correct size, correct error is returned + match Jwe::new(HEADER, KEY, "foo", CIPHER_TEXT, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidOverflow), } - // Errors if one of the parts is not valid BASE64URL - let invalid = "inv@alid value"; - if let Err((error_type, _)) = Jwe::new(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, invalid) { - assert_eq!(error_type, ErrorType::InvalidValue); - } else { - panic!("Should not have built JWE successfully.") + // When init_vector is not valid BASE64URL, correct error is returned + let not64 = "inv@alid value!!"; + match Jwe::new(HEADER, KEY, not64, CIPHER_TEXT, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidValue), } + } - // Errors if one of the parts exceeds max length + #[test] + fn jwe_validates_cipher_text_value_correctly() { + // When cipher_text is empty, correct error is returned + match Jwe::new(HEADER, KEY, INIT_VECTOR, "", AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidValue), + } + + // When cipher_text is bigger than max size, correct error is returned let too_long = (0..1025).map(|_| "X").collect::(); - if let Err((error_type, _)) = Jwe::new(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, &too_long) { - assert_eq!(error_type, ErrorType::InvalidOverflow); - } else { - panic!("Should not have built JWE successfully.") + match Jwe::new(HEADER, KEY, INIT_VECTOR, &too_long, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidOverflow), } - // Doesn't error if allowed elements are empty - assert!(Jwe::new(HEADER, "", "", CIPHER_TEXT, "").is_ok()); + // When cipher_text is not valid BASE64URL, correct error is returned + let not64 = "inv@alid value!"; + match Jwe::new(HEADER, KEY, INIT_VECTOR, not64, AUTH_TAG) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidValue), + } + } - // Doesn't error for the sample value - assert!(Jwe::new(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG).is_ok()); + #[test] + fn jwe_validates_auth_tag_value_correctly() { + // When auth_tag is empty, JWE is created + assert!(Jwe::new(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, "").is_ok()); + + // When auth_tag is not the correct size, correct error is returned + match Jwe::new(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, "foo") { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidOverflow), + } + + // When auth_tag is not valid BASE64URL, correct error is returned + let not64 = "inv@alid value!!!!!!!!"; + match Jwe::new(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, not64) { + Ok(_) => panic!("Should not have built JWE successfully."), + Err((error_type, _)) => assert_eq!(error_type, ErrorType::InvalidValue), + } } #[test] diff --git a/glean-core/tests/jwe.rs b/glean-core/tests/jwe.rs index f14190e17c..e79aa56a96 100644 --- a/glean-core/tests/jwe.rs +++ b/glean-core/tests/jwe.rs @@ -11,7 +11,13 @@ use glean_core::metrics::*; use glean_core::storage::StorageManager; use glean_core::{CommonMetricData, Lifetime}; -const SAMPLE_JWE_VALUE: &str = "eyJhbGciOiJFQ0RILUVTIiwia2lkIjoiMFZFRTdmT0txbFdHVGZrY0taRUJ2WWl3dkpMYTRUUGlJVGxXMGJOcDdqVSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InY3Q1FlRWtVQjMwUGwxV0tPMUZUZ25OQlNQdlFyNlh0UnZxT2kzSWdzNHciLCJ5IjoiNDBKVEpaQlMwOXpWNHpxb0hHZDI5NGFDeHRqcGU5a09reGhELVctUEZsSSJ9LCJlbmMiOiJBMjU2R0NNIn0..A_wzJya943vlHKFH.yq0JhkGZiZd6UiZK6goTcEf6i4gbbBeXxvq8QV5_nC4.Knl_sYSBrrP-aa54z6B6gA"; +const HEADER: &str = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ"; +const KEY: &str = "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg"; +const INIT_VECTOR: &str = "48V1_ALb6US04U3b"; +const CIPHER_TEXT: &str = + "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A"; +const AUTH_TAG: &str = "XFBoMYUZodetZdvTiFvSkQ"; +const JWE: &str = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ"; #[test] fn jwe_metric_is_generated_and_stored() { @@ -24,13 +30,13 @@ fn jwe_metric_is_generated_and_stored() { ..Default::default() }); - metric.set_with_compact_repr(&glean, SAMPLE_JWE_VALUE); + metric.set_with_compact_repr(&glean, JWE); let snapshot = StorageManager - .snapshot_as_json(glean.storage(), "core", true) + .snapshot_as_json(glean.storage(), "core", false) .unwrap(); assert_eq!( - json!({"jwe": {"local.jwe_metric": SAMPLE_JWE_VALUE }}), + json!({"jwe": {"local.jwe_metric": metric.test_get_value(&glean, "core") }}), snapshot ); } @@ -49,17 +55,59 @@ fn set_properly_sets_the_value_in_all_stores() { ..Default::default() }); - metric.set_with_compact_repr(&glean, SAMPLE_JWE_VALUE); + metric.set_with_compact_repr(&glean, JWE); // Check that the data was correctly set in each store. for store_name in store_names { let snapshot = StorageManager - .snapshot_as_json(glean.storage(), &store_name, true) + .snapshot_as_json(glean.storage(), &store_name, false) .unwrap(); assert_eq!( - json!({"jwe": {"local.jwe_metric": SAMPLE_JWE_VALUE}}), + json!({"jwe": {"local.jwe_metric": metric.test_get_value(&glean, &store_name) }}), snapshot ); } } + +#[test] +fn get_test_value_returns_the_period_delimited_string() { + let (glean, _t) = new_glean(None); + + let metric = JweMetric::new(CommonMetricData { + name: "jwe_metric".into(), + category: "local".into(), + send_in_pings: vec!["core".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }); + + metric.set_with_compact_repr(&glean, JWE); + + assert_eq!(metric.test_get_value(&glean, "core").unwrap(), JWE); +} + +#[test] +fn get_test_value_as_json_string_returns_the_expected_repr() { + let (glean, _t) = new_glean(None); + + let metric = JweMetric::new(CommonMetricData { + name: "jwe_metric".into(), + category: "local".into(), + send_in_pings: vec!["core".into()], + disabled: false, + lifetime: Lifetime::Ping, + ..Default::default() + }); + + metric.set_with_compact_repr(&glean, JWE); + + let expected_json = format!("{{\"header\":\"{}\",\"key\":\"{}\",\"init_vector\":\"{}\",\"cipher_text\":\"{}\",\"auth_tag\":\"{}\"}}", HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG); + assert_eq!( + metric + .test_get_value_as_json_string(&glean, "core") + .unwrap(), + expected_json + ); +}