Remove already-deprecated JWE metric.

It's already disabled in the glean_parser and thus cannot be used
anyway.
This commit is contained in:
Jan-Erik Rediger 2021-11-24 11:36:35 +01:00 коммит произвёл Jan-Erik Rediger
Родитель d32bb60065
Коммит 231a1e50b1
14 изменённых файлов: 3 добавлений и 1735 удалений

Просмотреть файл

@ -1,194 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package mozilla.telemetry.glean.private
import androidx.annotation.VisibleForTesting
import com.sun.jna.StringArray
import mozilla.telemetry.glean.Dispatchers
import mozilla.telemetry.glean.rust.LibGleanFFI
import mozilla.telemetry.glean.rust.getAndConsumeRustString
import mozilla.telemetry.glean.rust.toBoolean
import mozilla.telemetry.glean.rust.toByte
import mozilla.telemetry.glean.testing.ErrorType
import org.json.JSONObject
/**
* A representation of a JWE value.
*/
data class JweData(
val header: String,
val key: String,
val initVector: String,
val cipherText: String,
val authTag: String
)
/**
* This implements the developer facing API for recording JWE metrics.
*
* Instances of this class type are automatically generated by the parsers at build time,
* allowing developers to record values that were previously registered in the metrics.yaml file.
*
* The JWE API exposes the [set] and [setWithCompactRepresentation] methods,
* which take care of validating the input data.
*/
class JweMetricType internal constructor(
private var handle: Long,
private val disabled: Boolean,
private val sendInPings: List<String>
) {
/**
* The public constructor used by automatically generated metrics.
*/
constructor(
disabled: Boolean,
category: String,
lifetime: Lifetime,
name: String,
sendInPings: List<String>
) : this(handle = 0, disabled = disabled, sendInPings = sendInPings) {
val ffiPingsList = StringArray(sendInPings.toTypedArray(), "utf-8")
this.handle = LibGleanFFI.INSTANCE.glean_new_jwe_metric(
category = category,
name = name,
send_in_pings = ffiPingsList,
send_in_pings_len = sendInPings.size,
lifetime = lifetime.ordinal,
disabled = disabled.toByte()
)
}
/**
* Set a JWE value.
*
* @param value The compact representation of a JWE value.
*/
fun setWithCompactRepresentation(value: String) {
if (disabled) {
return
}
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_jwe_set_with_compact_representation(this@JweMetricType.handle, value)
}
}
/**
* Build a JWE value from its elements and set to it.
*
* @param header A variable-size JWE protected header.
* @param key A variable-size encrypted key.
* This can be an empty octet sequence.
* @param initVector A fixed-size, 96-bit, base64 encoded Jwe initialization vector.
* If not required by the encryption algorithm, can be an empty octet sequence.
* @param cipherText The variable-size base64 encoded cipher text.
* @param authTag A fixed-size, 132-bit, base64 encoded authentication tag.
* Can be an empty octet sequence.
*/
fun set(header: String, key: String, initVector: String, cipherText: String, authTag: String) {
if (disabled) {
return
}
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_jwe_set(this@JweMetricType.handle, header, key, initVector, cipherText, authTag)
}
}
/**
* Tests whether a value is stored for the metric for testing purposes only. This function will
* attempt to await the last task (if any) writing to the the metric's storage engine before
* returning a value.
*
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return true if metric value exists, otherwise false
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testHasValue(pingName: String = sendInPings.first()): Boolean {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
return LibGleanFFI.INSTANCE
.glean_jwe_test_has_value(this.handle, pingName)
.toBoolean()
}
/**
* Returns the stored value for testing purposes only. This function will attempt to await the
* last task (if any) writing to the the metric's storage engine before returning a value.
*
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return value of the stored metric
* @throws [NullPointerException] if no value is stored
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testGetValue(pingName: String = sendInPings.first()): JweData {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
if (!testHasValue(pingName)) {
throw NullPointerException("Metric has no value")
}
val ptr = LibGleanFFI.INSTANCE.glean_jwe_test_get_value_as_json_string(this.handle, pingName)!!
val json = JSONObject(ptr.getAndConsumeRustString())
return JweData(
header = json.get("header") as String,
key = json.get("key") as String,
initVector = json.get("init_vector") as String,
cipherText = json.get("cipher_text") as String,
authTag = json.get("auth_tag") as String
)
}
/**
* Returns the stored value in the compact representation for testing purposes only.
* This function will attempt to await the last task (if any)
* writing to the the metric's storage engine before returning a value.
*
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return value of the stored metric
* @throws [NullPointerException] if no value is stored
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testGetCompactRepresentation(pingName: String = sendInPings.first()): String {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
if (!testHasValue(pingName)) {
throw NullPointerException("Metric has no value")
}
val ptr = LibGleanFFI.INSTANCE.glean_jwe_test_get_value(this.handle, pingName)!!
return ptr.getAndConsumeRustString()
}
/**
* Returns the number of errors recorded for the given metric.
*
* @param errorType The type of the error recorded.
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return the number of errors recorded for the metric.
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testGetNumRecordedErrors(errorType: ErrorType, pingName: String = sendInPings.first()): Int {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
return LibGleanFFI.INSTANCE.glean_jwe_test_get_num_recorded_errors(
this.handle, errorType.ordinal, pingName
)
}
}

Просмотреть файл

@ -1,163 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This file is based on the tests in the Glean android-components implentation.
*
* Care should be taken to not reorder elements in this file so it will be easier
* to track changes in Glean android-components.
*/
package mozilla.telemetry.glean.private
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.telemetry.glean.testing.ErrorType
import mozilla.telemetry.glean.testing.GleanTestRule
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.lang.NullPointerException
@RunWith(AndroidJUnit4::class)
@Suppress("MaxLineLength")
class JweMetricTypeTest {
companion object {
const val HEADER: String = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ"
const val KEY: String = "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg"
const val INIT_VECTOR: String = "48V1_ALb6US04U3b"
const val CIPHER_TEXT: String = "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A"
const val AUTH_TAG: String = "XFBoMYUZodetZdvTiFvSkQ"
const val JWE: String = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ"
const val MINIMUM_JWE: String = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ...5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A."
}
@get:Rule
val gleanRule = GleanTestRule(ApplicationProvider.getApplicationContext())
@Test
fun `The API saves to its storage engine`() {
// Define a 'jweMetric' jwe metric, which will be stored in "store1"
val jweMetric = JweMetricType(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
name = "jwe_metric",
sendInPings = listOf("store1")
)
// Record two JWEs of the same type, with a little delay.
jweMetric.set(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG)
// Check that data was properly recorded.
assertTrue(jweMetric.testHasValue())
assertEquals(JWE, jweMetric.testGetCompactRepresentation())
jweMetric.set(HEADER, "", "", CIPHER_TEXT, "")
// Check that data was properly recorded.
assertTrue(jweMetric.testHasValue())
assertEquals(MINIMUM_JWE, jweMetric.testGetCompactRepresentation())
}
@Test
fun `disabled JWEs must not record data`() {
// Define a 'jweMetric' jwe metric, which will be stored in "store1". It's disabled
// so it should not record anything.
val jweMetric = JweMetricType(
disabled = true,
category = "telemetry",
lifetime = Lifetime.Application,
name = "jwe_metric",
sendInPings = listOf("store1")
)
// Attempt to store the JWE.
jweMetric.set(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG)
// Check that nothing was recorded.
assertFalse(
"JWEs must not be recorded if they are disabled",
jweMetric.testHasValue()
)
}
@Test(expected = NullPointerException::class)
fun `testGetValue() throws NullPointerException if nothing is stored`() {
val jweMetric = JweMetricType(
disabled = true,
category = "telemetry",
lifetime = Lifetime.Application,
name = "jwe_metric",
sendInPings = listOf("store1")
)
jweMetric.testGetValue()
}
@Test
fun `testGetValue() returns correct JweData representation`() {
// Define a 'jweMetric' jwe metric, which will be stored in "store1".
val jweMetric = JweMetricType(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
name = "jwe_metric",
sendInPings = listOf("store1")
)
// Attempt to store the JWE.
jweMetric.set(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG)
val data = jweMetric.testGetValue()
assertEquals(data.header, HEADER)
assertEquals(data.key, KEY)
assertEquals(data.initVector, INIT_VECTOR)
assertEquals(data.cipherText, CIPHER_TEXT)
assertEquals(data.authTag, AUTH_TAG)
}
@Test
fun `The API saves to secondary pings`() {
// Define a 'jweMetric' jwe metric, which will be stored in "store1" and "store2"
val jweMetric = JweMetricType(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
name = "jwe_metric",
sendInPings = listOf("store1", "store2")
)
// Record two JWEs, with a little delay.
jweMetric.set(HEADER, KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG)
// Check that data was properly recorded in the second ping.
assertTrue(jweMetric.testHasValue("store2"))
assertEquals(JWE, jweMetric.testGetCompactRepresentation("store2"))
jweMetric.set(HEADER, "", "", CIPHER_TEXT, "")
// Check that data was properly recorded in the second ping.
assertTrue(jweMetric.testHasValue("store2"))
assertEquals(MINIMUM_JWE, jweMetric.testGetCompactRepresentation())
}
@Test
fun `Trying to set invalid values records errors`() {
// Define a 'jweMetric' jwe metric, which will be stored in "store1" and "store2"
val jweMetric = JweMetricType(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
name = "jwe_metric",
sendInPings = listOf("store1", "store2")
)
// Too long elements should yield a InvalidOverflow error
jweMetric.set("X".repeat(1025), KEY, INIT_VECTOR, CIPHER_TEXT, AUTH_TAG)
assertEquals(1, jweMetric.testGetNumRecordedErrors(ErrorType.InvalidOverflow))
// Invalid compact string representation yield a InvalidValue error
jweMetric.setWithCompactRepresentation("")
assertEquals(1, jweMetric.testGetNumRecordedErrors(ErrorType.InvalidValue))
}
}

Просмотреть файл

@ -1,83 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::os::raw::c_char;
use ffi_support::FfiStr;
use crate::ffi_string_ext::FallibleToString;
use crate::{define_metric, handlemap_ext::HandleMapExtension, with_glean_value, Lifetime};
define_metric!(JweMetric => JWE_METRICS {
new -> glean_new_jwe_metric(),
test_get_num_recorded_errors -> glean_jwe_test_get_num_recorded_errors,
destroy -> glean_destroy_jwe_metric,
});
#[no_mangle]
pub extern "C" fn glean_jwe_set_with_compact_representation(metric_id: u64, value: FfiStr) {
with_glean_value(|glean| {
JWE_METRICS.call_with_log(metric_id, |metric| {
let value = value.to_string_fallible()?;
metric.set_with_compact_representation(glean, value);
Ok(())
})
})
}
#[no_mangle]
pub extern "C" fn glean_jwe_set(
metric_id: u64,
header: FfiStr,
key: FfiStr,
init_vector: FfiStr,
cipher_text: FfiStr,
auth_tag: FfiStr,
) {
with_glean_value(|glean| {
JWE_METRICS.call_with_log(metric_id, |metric| {
let header = header.to_string_fallible()?;
let key = key.to_string_fallible()?;
let init_vector = init_vector.to_string_fallible()?;
let cipher_text = cipher_text.to_string_fallible()?;
let auth_tag = auth_tag.to_string_fallible()?;
metric.set(glean, header, key, init_vector, cipher_text, auth_tag);
Ok(())
})
})
}
#[no_mangle]
pub extern "C" fn glean_jwe_test_has_value(metric_id: u64, storage_name: FfiStr) -> u8 {
with_glean_value(|glean| {
JWE_METRICS.call_infallible(metric_id, |metric| {
metric
.test_get_value(glean, storage_name.as_str())
.is_some()
})
})
}
#[no_mangle]
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()
})
})
}
#[no_mangle]
pub extern "C" fn glean_jwe_test_get_value_as_json_string(
metric_id: u64,
storage_name: FfiStr,
) -> *mut c_char {
with_glean_value(|glean| {
JWE_METRICS.call_infallible(metric_id, |metric| {
metric
.test_get_value_as_json_string(glean, storage_name.as_str())
.unwrap()
})
})
}

Просмотреть файл

@ -32,7 +32,6 @@ mod event;
mod ffi_string_ext;
mod from_raw;
mod handlemap_ext;
mod jwe;
mod labeled;
mod memory_distribution;
pub mod ping_type;

Просмотреть файл

@ -25,8 +25,6 @@
1FB8F8382326EABD00618E47 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB8F8372326EABD00618E47 /* ConfigurationTests.swift */; };
1FD4527523395B4500F4C7E8 /* UuidMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD4527423395B4500F4C7E8 /* UuidMetric.swift */; };
1FD4527723395EEB00F4C7E8 /* UuidMetricTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD4527623395EEB00F4C7E8 /* UuidMetricTests.swift */; };
5FE1C4CF24C0A676000CAA1A /* JweMetricTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE1C4CE24C0A676000CAA1A /* JweMetricTests.swift */; };
5FE1C4D124C0A6A2000CAA1A /* JweMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE1C4D024C0A6A2000CAA1A /* JweMetric.swift */; };
97C5C1A423708C6700B79C93 /* ErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97C5C1A323708C6700B79C93 /* ErrorType.swift */; };
AC06529C26E032E300D92D5E /* QuantityMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC06529B26E032E300D92D5E /* QuantityMetric.swift */; };
AC06529E26E034BF00D92D5E /* QuantityMetricTypeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC06529D26E034BF00D92D5E /* QuantityMetricTypeTest.swift */; };
@ -119,8 +117,6 @@
1FB8F8372326EABD00618E47 /* ConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = "<group>"; };
1FD4527423395B4500F4C7E8 /* UuidMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UuidMetric.swift; sourceTree = "<group>"; };
1FD4527623395EEB00F4C7E8 /* UuidMetricTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UuidMetricTests.swift; sourceTree = "<group>"; };
5FE1C4CE24C0A676000CAA1A /* JweMetricTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JweMetricTests.swift; sourceTree = "<group>"; };
5FE1C4D024C0A6A2000CAA1A /* JweMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JweMetric.swift; sourceTree = "<group>"; };
97C5C1A323708C6700B79C93 /* ErrorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorType.swift; sourceTree = "<group>"; };
AC06529B26E032E300D92D5E /* QuantityMetric.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuantityMetric.swift; sourceTree = "<group>"; };
AC06529D26E034BF00D92D5E /* QuantityMetricTypeTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuantityMetricTypeTest.swift; sourceTree = "<group>"; };
@ -354,7 +350,6 @@
BF6C53B1232F870C00E3B43A /* Ping.swift */,
BF30FDC3233260B500840607 /* TimespanMetric.swift */,
BF30FDC72332640400840607 /* TimeUnit.swift */,
5FE1C4D024C0A6A2000CAA1A /* JweMetric.swift */,
BF10007D23548AF400064051 /* MemoryUnit.swift */,
BFAED5072369751100DF293D /* StringListMetric.swift */,
BF89055E232BC213003CA2BA /* StringMetric.swift */,
@ -373,7 +368,6 @@
isa = PBXGroup;
children = (
AC06529D26E034BF00D92D5E /* QuantityMetricTypeTest.swift */,
5FE1C4CE24C0A676000CAA1A /* JweMetricTests.swift */,
1F6A8FF1233C068A007837D5 /* BooleanMetricTypeTest.swift */,
BF43A8CC232A615200545310 /* CounterMetricTests.swift */,
1F6A8FF5233C1555007837D5 /* DatetimeMetricTypeTests.swift */,
@ -609,7 +603,6 @@
BF43A8C7232A4BA400545310 /* CounterMetric.swift in Sources */,
BFFE33AB232927C3005348FE /* Utils.swift in Sources */,
BFE1CDC4233B63A70019EE47 /* LabeledMetric.swift in Sources */,
5FE1C4D124C0A6A2000CAA1A /* JweMetric.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -639,7 +632,6 @@
BFFE183A2350A61F0068D97B /* TimingDistributionMetricTests.swift in Sources */,
BF3DE3A02243A2F20018E23F /* GleanTests.swift in Sources */,
BF2E57072334BD7600364D92 /* EventMetricTests.swift in Sources */,
5FE1C4CF24C0A676000CAA1A /* JweMetricTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

Просмотреть файл

@ -1,179 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
/// A representation of a JWE value.
public struct JweData {
let header: String
let key: String
let initVector: String
let cipherText: String
let authTag: String
}
/// This implements the developer facing API for recording JWE metrics.
///
/// Instances of this class type are automatically generated by the parsers at build time,
/// allowing developers to record values that were previously registered in the metrics.yaml file.
///
/// The JWE API exposes the `JweMetricType.set(_:)` and `JweMetricType.setWithCompactRepresentation(_:)` methods,
/// which takes care of validating the input data.
public class JweMetricType {
let handle: UInt64
let disabled: Bool
let sendInPings: [String]
/// The public constructor used by automatically generated metrics.
public init(category: String, name: String, sendInPings: [String], lifetime: Lifetime, disabled: Bool) {
self.disabled = disabled
self.sendInPings = sendInPings
self.handle = withArrayOfCStrings(sendInPings) { pingArray in
glean_new_jwe_metric(
category,
name,
pingArray,
Int32(sendInPings.count),
lifetime.rawValue,
disabled.toByte()
)
}
}
/// Destroy this metric.
deinit {
if self.handle != 0 {
glean_destroy_jwe_metric(self.handle)
}
}
/// Set a JWE value.
///
/// - parameters:
/// * header: value The [`compact representation`](https://tools.ietf.org/html/rfc7516#appendix-A.2.7) of a JWE value.
public func setWithCompactRepresentation(_ value: String) {
guard !self.disabled else { return }
Dispatchers.shared.launchAPI {
glean_jwe_set_with_compact_representation(self.handle, value)
}
}
/// Build a JWE value from it's elements and set to it.
///
/// - parameters:
/// * header: A variable-size JWE protected header.
/// * key: A variable-size encrypted key.
/// This can be an empty octet sequence.
/// * initVector: A fixed-size, 96-bit, base64 encoded Jwe initialization vector.
/// If not required by the encryption algorithm, can be an empty octet sequence.
/// * cipherText: The variable-size base64 encoded cipher text.
/// * authTag: A fixed-size, 132-bit, base64 encoded authentication tag.
/// Can be an empty octet sequence.
public func set(_ header: String, _ key: String, _ initVector: String, _ cipherText: String, _ authTag: String) {
guard !self.disabled else { return }
Dispatchers.shared.launchAPI {
glean_jwe_set(self.handle, header, key, initVector, cipherText, authTag)
}
}
/// Tests whether a value is stored for the metric for testing purposes only. This function will
/// attempt to await the last task (if any) writing to the the metric's storage engine before
/// returning a value.
///
/// - parameters:
/// * pingName: represents the name of the ping to retrieve the metric for.
/// Defaults to the first value in `sendInPings`.
/// - returns: true if metric value exists, otherwise false
public func testHasValue(_ pingName: String? = nil) -> Bool {
Dispatchers.shared.assertInTestingMode()
let pingName = pingName ?? self.sendInPings[0]
return glean_jwe_test_has_value(self.handle, pingName).toBool()
}
// swiftlint:disable force_cast
/// Returns the stored value for testing purposes only. This function will attempt to await the
/// last task (if any) writing to the the metric's storage engine before returning a value.
///
/// Throws a "Missing value" exception if no value is stored.
///
/// - parameters:
/// * pingName: represents the name of the ping to retrieve the metric for.
/// Defaults to the first value in `sendInPings`.
///
/// - returns: value of the stored metric
public func testGetValue(_ pingName: String? = nil) throws -> JweData {
Dispatchers.shared.assertInTestingMode()
let pingName = pingName ?? self.sendInPings[0]
if !testHasValue(pingName) {
throw "Missing value"
}
var data: JweData?
let jsonString = String(freeingGleanString: glean_jwe_test_get_value_as_json_string(self.handle, pingName))
if let jsonData: Data = jsonString.data(using: .utf8, allowLossyConversion: false) {
if let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
data = JweData(
header: json["header"] as! String,
key: json["key"] as! String,
initVector: json["init_vector"] as! String,
cipherText: json["cipher_text"] as! String,
authTag: json["auth_tag"] as! String
)
}
}
return data!
}
// swiftlint:enable force_cast
/// Returns the stored value in the compact representation for testing purposes only.
/// This function will attempt to await the last task (if any)
/// writing to the metric's storage engine before returning a value.
///
/// Throws a "Missing value" exception if no value is stored.
///
/// - parameters:
/// * pingName: represents the name of the ping to retrieve the metric for.
/// Defaults to the first value in `sendInPings`.
///
/// - returns: value of the stored metric
public func testGetCompactRepresentation(_ pingName: String? = nil) throws -> String {
Dispatchers.shared.assertInTestingMode()
let pingName = pingName ?? self.sendInPings[0]
if !testHasValue(pingName) {
throw "Missing value"
}
return String(freeingGleanString: glean_jwe_test_get_value(self.handle, pingName))
}
/// Returns the number of errors recorded for the given metric.
///
/// - parameters:
/// * errorType: The type of error recorded.
/// * pingName: represents the name of the ping to retrieve the metric for.
/// Defaults to the first value in `sendInPings`.
///
/// - returns: The number of errors recorded for the metric for the given error type.
public func testGetNumRecordedErrors(_ errorType: ErrorType, pingName: String? = nil) -> Int32 {
Dispatchers.shared.assertInTestingMode()
let pingName = pingName ?? self.sendInPings[0]
return glean_jwe_test_get_num_recorded_errors(
self.handle,
errorType.rawValue,
pingName
)
}
}

Просмотреть файл

@ -1,137 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@testable import Glean
import XCTest
// swiftlint:disable force_cast
// REASON: Used in a test
class JweMetricTests: XCTestCase {
// swiftlint:disable line_length
private let header: String = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ"
private let key: String = "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg"
private let initVector: String = "48V1_ALb6US04U3b"
private let cipherText: String = "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A"
private let authTag: String = "XFBoMYUZodetZdvTiFvSkQ"
private let jwe: String = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ"
private let minimumJwe: String = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ...5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A."
// swiftlint:enable line_length
override func setUp() {
resetGleanDiscardingInitialPings(testCase: self, tag: "JweMetricTests")
}
override func tearDown() {
tearDownStubs()
}
func testJweSavesToStorage() {
let jweMetric = JweMetricType(
category: "telemetry",
name: "jwe_metric",
sendInPings: ["store1"],
lifetime: .application,
disabled: false
)
XCTAssertFalse(jweMetric.testHasValue())
jweMetric.set(self.header, self.key, self.initVector, self.cipherText, self.authTag)
XCTAssert(jweMetric.testHasValue())
XCTAssertEqual(self.jwe, try jweMetric.testGetCompactRepresentation())
jweMetric.set(self.header, "", "", self.cipherText, "")
XCTAssert(jweMetric.testHasValue())
XCTAssertEqual(self.minimumJwe, try jweMetric.testGetCompactRepresentation())
}
func testJweMustNotRecordIfDisabled() {
let jweMetric = JweMetricType(
category: "telemetry",
name: "jwe_metric",
sendInPings: ["store1"],
lifetime: .application,
disabled: true
)
XCTAssertFalse(jweMetric.testHasValue())
jweMetric.setWithCompactRepresentation(self.jwe)
XCTAssertFalse(jweMetric.testHasValue(), "JWEs must not be recorded if they are disabled")
}
func testJweGetValueThrowsExceptionIfNothingIsStored() {
let jweMetric = JweMetricType(
category: "telemetry",
name: "jwe_metric",
sendInPings: ["store1"],
lifetime: .application,
disabled: false
)
XCTAssertThrowsError(try jweMetric.testGetValue()) { error in
XCTAssertEqual(error as! String, "Missing value")
}
}
func testJweGetValueReturnsCorrectJweDataRepresentation() {
let jweMetric = JweMetricType(
category: "telemetry",
name: "jwe_metric",
sendInPings: ["store1"],
lifetime: .application,
disabled: false
)
jweMetric.set(self.header, self.key, self.initVector, self.cipherText, self.authTag)
let data = try! jweMetric.testGetValue()
XCTAssertEqual(data.header, self.header)
XCTAssertEqual(data.key, self.key)
XCTAssertEqual(data.initVector, self.initVector)
XCTAssertEqual(data.cipherText, self.cipherText)
XCTAssertEqual(data.authTag, self.authTag)
}
func testJweSavesToSecondaryPings() {
let jweMetric = JweMetricType(
category: "telemetry",
name: "jwe_metric",
sendInPings: ["store1", "store2"],
lifetime: .application,
disabled: false
)
jweMetric.set(self.header, self.key, self.initVector, self.cipherText, self.authTag)
XCTAssert(jweMetric.testHasValue("store2"))
XCTAssertEqual(jwe, try jweMetric.testGetCompactRepresentation())
jweMetric.set(self.header, "", "", self.cipherText, "")
XCTAssert(jweMetric.testHasValue())
XCTAssertEqual(self.minimumJwe, try jweMetric.testGetCompactRepresentation())
}
func testSettingInvalidValuesRecordsErrors() {
let jweMetric = JweMetricType(
category: "telemetry",
name: "jwe_metric",
sendInPings: ["store1"],
lifetime: .application,
disabled: false
)
// Too long elements should yield a InvalidOverflow error
jweMetric.set(String(repeating: "X", count: 1025), self.key, self.initVector, self.cipherText, self.authTag)
XCTAssertEqual(1, jweMetric.testGetNumRecordedErrors(ErrorType.invalidOverflow))
// Invalid compact string representation yield a InvalidValue error
jweMetric.setWithCompactRepresentation("")
XCTAssertEqual(1, jweMetric.testGetNumRecordedErrors(ErrorType.invalidValue))
}
}

Просмотреть файл

@ -1,221 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from typing import List, Optional
import json
from .. import _ffi
from .._dispatcher import Dispatcher
from ..testing import ErrorType
from .lifetime import Lifetime
class JweData:
"""
A representation of a JWE value.
"""
def __init__(
self, header: str, key: str, init_vector: str, cipher_text: str, auth_tag: str
):
self.header = header
self.key = key
self.init_vector = init_vector
self.cipher_text = cipher_text
self.auth_tag = auth_tag
class JweMetricType:
"""
This implements the developer facing API for recording JWE metrics.
Instances of this class type are automatically generated by
`glean.load_metrics`, allowing developers to record values that were
previously registered in the metrics.yaml file.
The string API exposes the `JweMetricType.set` and
`JweMetricType.setWithCompactRepresentation` methods,
which take care of validating the input data
and making sure that limits are enforced.
"""
def __init__(
self,
disabled: bool,
category: str,
lifetime: Lifetime,
name: str,
send_in_pings: List[str],
):
self._disabled = disabled
self._send_in_pings = send_in_pings
self._handle = _ffi.lib.glean_new_jwe_metric(
_ffi.ffi_encode_string(category),
_ffi.ffi_encode_string(name),
_ffi.ffi_encode_vec_string(send_in_pings),
len(send_in_pings),
lifetime.value,
disabled,
)
def __del__(self):
if getattr(self, "_handle", 0) != 0:
_ffi.lib.glean_destroy_jwe_metric(self._handle)
def set_with_compact_representation(self, value: str):
"""
Set to the specified JWE value.
Args:
value (str): the compact representation of a JWE value.
"""
if self._disabled:
return
@Dispatcher.launch
def set():
_ffi.lib.glean_jwe_set_with_compact_representation(
self._handle, _ffi.ffi_encode_string(value)
)
def set(
self, header: str, key: str, init_vector: str, cipher_text: str, auth_tag: str
) -> None:
"""
Build a JWE value from its elements and set to it.
Args:
header (str): A variable-size JWE protected header.
key (str): A variable-size encrypted key.
This can be an empty octet sequence.
init_vector (str): A fixed-size, 96-bit, base64 encoded Jwe initialization vector.
If not required by the encryption algorithm, can be an empty octet sequence.
cipher_text (str): The variable-size base64 encoded cipher text.
auth_tag (str): A fixed-size, 132-bit, base64 encoded authentication tag.
Can be an empty octet sequence.
"""
if self._disabled:
return
@Dispatcher.launch
def set():
_ffi.lib.glean_jwe_set(
self._handle,
_ffi.ffi_encode_string(header),
_ffi.ffi_encode_string(key),
_ffi.ffi_encode_string(init_vector),
_ffi.ffi_encode_string(cipher_text),
_ffi.ffi_encode_string(auth_tag),
)
def test_has_value(self, ping_name: Optional[str] = None) -> bool:
"""
Tests whether a value is stored for the metric for testing purposes
only.
Args:
ping_name (str): (default: first value in send_in_pings) The name
of the ping to retrieve the metric for.
Returns:
has_value (bool): True if the metric value exists.
"""
if ping_name is None:
ping_name = self._send_in_pings[0]
return bool(
_ffi.lib.glean_jwe_test_has_value(
self._handle, _ffi.ffi_encode_string(ping_name)
)
)
def test_get_value(self, ping_name: Optional[str] = None) -> JweData:
"""
Returns the stored value for testing purposes only.
Args:
ping_name (str): (default: first value in send_in_pings) The name
of the ping to retrieve the metric for.
Returns:
value (JweData): value of the stored metric.
"""
if ping_name is None:
ping_name = self._send_in_pings[0]
if not self.test_has_value(ping_name):
raise ValueError("metric has no value")
json_payload = json.loads(
_ffi.ffi_decode_string(
_ffi.lib.glean_jwe_test_get_value_as_json_string(
self._handle, _ffi.ffi_encode_string(ping_name)
)
)
)
return JweData(
json_payload["header"],
json_payload["key"],
json_payload["init_vector"],
json_payload["cipher_text"],
json_payload["auth_tag"],
)
def test_get_compact_representation(self, ping_name: Optional[str] = None) -> str:
"""
Returns the stored value as the compact representation
for testing purposes only.
Args:
ping_name (str): (default: first value in send_in_pings) The name
of the ping to retrieve the metric for.
Returns:
value (JweData): value of the stored metric.
"""
if ping_name is None:
ping_name = self._send_in_pings[0]
if not self.test_has_value(ping_name):
raise ValueError("metric has no value")
return _ffi.ffi_decode_string(
_ffi.lib.glean_jwe_test_get_value(
self._handle, _ffi.ffi_encode_string(ping_name)
)
)
def test_get_num_recorded_errors(
self, error_type: ErrorType, ping_name: Optional[str] = None
) -> int:
"""
Returns the number of errors recorded for the given metric.
Args:
error_type (ErrorType): The type of error recorded.
ping_name (str): (default: first value in send_in_pings) The name
of the ping to retrieve the metric for.
Returns:
num_errors (int): The number of errors recorded for the metric for
the given error type.
"""
if ping_name is None:
ping_name = self._send_in_pings[0]
return _ffi.lib.glean_jwe_test_get_num_recorded_errors(
self._handle,
error_type.value,
_ffi.ffi_encode_string(ping_name),
)
__all__ = ["JweMetricType"]

Просмотреть файл

@ -1,108 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# flake8: noqa E501
from glean import metrics
from glean.metrics import Lifetime
from glean import testing
header = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ"
key = "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg"
init_vector = "48V1_ALb6US04U3b"
cipher_text = "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A"
auth_tag = "XFBoMYUZodetZdvTiFvSkQ"
jwe = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ"
minimum_jwe = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ...5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A."
def test_the_api_saves_to_its_storage_engine():
jwe_metric = metrics.JweMetricType(
disabled=False,
category="telemetry",
lifetime=Lifetime.APPLICATION,
name="jwe_metric",
send_in_pings=["store1"],
)
jwe_metric.set(header, key, init_vector, cipher_text, auth_tag)
assert jwe_metric.test_has_value()
assert jwe == jwe_metric.test_get_compact_representation()
jwe_metric.set(header, "", "", cipher_text, "")
assert jwe_metric.test_has_value()
assert minimum_jwe == jwe_metric.test_get_compact_representation()
def test_disabled_jwes_must_not_record_data():
jwe_metric = metrics.JweMetricType(
disabled=True,
category="telemetry",
lifetime=Lifetime.APPLICATION,
name="jwe_metric",
send_in_pings=["store1"],
)
jwe_metric.set(header, key, init_vector, cipher_text, auth_tag)
assert not jwe_metric.test_has_value()
def test_jwe_get_value_returns_correct_jwe_data_representation():
jwe_metric = metrics.JweMetricType(
disabled=False,
category="telemetry",
lifetime=Lifetime.APPLICATION,
name="jwe_metric",
send_in_pings=["store1"],
)
jwe_metric.set(header, key, init_vector, cipher_text, auth_tag)
data = jwe_metric.test_get_value()
assert data.header == header
assert data.key == key
assert data.init_vector == init_vector
assert data.cipher_text == cipher_text
assert data.auth_tag == auth_tag
def test_the_api_saves_to_secondary_pings():
jwe_metric = metrics.JweMetricType(
disabled=False,
category="telemetry",
lifetime=Lifetime.APPLICATION,
name="jwe_metric",
send_in_pings=["store1", "store2"],
)
jwe_metric.set(header, key, init_vector, cipher_text, auth_tag)
assert jwe_metric.test_has_value("store2")
assert jwe == jwe_metric.test_get_compact_representation("store2")
jwe_metric.set(header, "", "", cipher_text, "")
assert jwe_metric.test_has_value("store2")
assert minimum_jwe == jwe_metric.test_get_compact_representation("store2")
def test_setting_invalid_values_record_errors():
jwe_metric = metrics.JweMetricType(
disabled=False,
category="telemetry",
lifetime=Lifetime.APPLICATION,
name="jwe_metric",
send_in_pings=["store1", "store2"],
)
jwe_metric.set("X" * 1025, key, init_vector, cipher_text, auth_tag)
assert 1 == jwe_metric.test_get_num_recorded_errors(
testing.ErrorType.INVALID_OVERFLOW
)
jwe_metric.set_with_compact_representation("")
assert 1 == jwe_metric.test_get_num_recorded_errors(testing.ErrorType.INVALID_VALUE)

Просмотреть файл

@ -1,469 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::fmt;
use std::str::FromStr;
use serde::Serialize;
use crate::error_recording::{record_error, ErrorType};
use crate::metrics::{Metric, MetricType};
use crate::storage::StorageManager;
use crate::CommonMetricData;
use crate::Glean;
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/rfc7515#section-2),
/// > the BASE64URL encoding used by JWE discards any padding,
/// > that is why we can ignore that for this validation.
///
/// The regex crate isn't used here because it adds to the binary size,
/// and the Glean SDK doesn't use regular expressions anywhere else.
fn validate_base64url_encoding(value: &str) -> bool {
let mut iter = value.chars();
loop {
match iter.next() {
// We are done, so the whole expression is valid.
None => return true,
// Valid characters.
Some('_') | Some('-') | Some('a'..='z') | Some('A'..='Z') | Some('0'..='9') => (),
// An invalid character.
Some(_) => return false,
}
}
}
/// 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 to prevent abuses 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,
}
// IMPORTANT:
//
// When changing this implementation, make sure all the operations are
// also declared in the related trait in `../traits/`.
impl Jwe {
/// Create a new JWE struct.
fn new<S: Into<String>>(
header: S,
key: S,
init_vector: S,
cipher_text: S,
auth_tag: S,
) -> Result<Self, (ErrorType, String)> {
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)?;
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,
key,
init_vector,
cipher_text,
auth_tag,
})
}
fn validate_base64url_encoding(
name: &str,
value: String,
) -> Result<String, (ErrorType, String)> {
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<String, (ErrorType, String)> {
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<String, (ErrorType, String)> {
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<String, (ErrorType, String)> {
// 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.
impl FromStr for Jwe {
type Err = (ErrorType, String);
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut elements: Vec<&str> = s.split('.').collect();
if elements.len() != 5 {
return Err((
ErrorType::InvalidValue,
"JWE value is not formatted as expected.".into(),
));
}
// Consume the vector extracting each part of the JWE from it.
//
// Safe unwraps, we already defined that the slice has five elements.
let auth_tag = elements.pop().unwrap();
let cipher_text = elements.pop().unwrap();
let init_vector = elements.pop().unwrap();
let key = elements.pop().unwrap();
let header = elements.pop().unwrap();
Self::new(header, key, init_vector, cipher_text, auth_tag)
}
}
/// Trait implementation to print the Jwe struct as the proper JWE [`compact representation`](https://tools.ietf.org/html/rfc7516#appendix-A.2.7).
impl fmt::Display for Jwe {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}.{}.{}.{}.{}",
self.header, self.key, self.init_vector, self.cipher_text, self.auth_tag
)
}
}
/// A JWE metric.
///
/// This metric will be work as a "transport" for JWE encrypted data.
///
/// The actual encrypti on is done somewhere else,
/// Glean must only make sure the data is valid JWE.
#[derive(Clone, Debug)]
pub struct JweMetric {
meta: CommonMetricData,
}
impl MetricType for JweMetric {
fn meta(&self) -> &CommonMetricData {
&self.meta
}
}
impl JweMetric {
/// Creates a new JWE metric.
pub fn new(meta: CommonMetricData) -> Self {
Self { meta }
}
/// Sets to the specified JWE value.
///
/// # Arguments
///
/// * `glean` - the Glean instance this metric belongs to.
/// * `value` - the [`compact representation`](https://tools.ietf.org/html/rfc7516#appendix-A.2.7) of a JWE value.
pub fn set_with_compact_representation<S: Into<String>>(&self, glean: &Glean, value: S) {
if !self.should_record(glean) {
return;
}
let value = value.into();
match Jwe::from_str(&value) {
Ok(_) => glean
.storage()
.record(glean, &self.meta, &Metric::Jwe(value)),
Err((error_type, msg)) => record_error(glean, &self.meta, error_type, msg, None),
};
}
/// Builds a JWE value from its elements and set to it.
///
/// # Arguments
///
/// * `glean` - the Glean instance this metric belongs to.
/// * `header` - the JWE Protected Header element.
/// * `key` - the JWE Encrypted Key element.
/// * `init_vector` - the JWE Initialization Vector element.
/// * `cipher_text` - the JWE Ciphertext element.
/// * `auth_tag` - the JWE Authentication Tag element.
pub fn set<S: Into<String>>(
&self,
glean: &Glean,
header: S,
key: S,
init_vector: S,
cipher_text: S,
auth_tag: S,
) {
if !self.should_record(glean) {
return;
}
match Jwe::new(header, key, init_vector, cipher_text, auth_tag) {
Ok(jwe) => glean
.storage()
.record(glean, &self.meta, &Metric::Jwe(jwe.to_string())),
Err((error_type, msg)) => record_error(glean, &self.meta, error_type, msg, None),
};
}
/// **Test-only API (exported for FFI purposes).**
///
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
pub fn test_get_value(&self, glean: &Glean, storage_name: &str) -> Option<String> {
match StorageManager.snapshot_metric_for_test(
glean.storage(),
storage_name,
&self.meta.identifier(glean),
self.meta.lifetime,
) {
Some(Metric::Jwe(b)) => Some(b),
_ => None,
}
}
/// **Test-only API (exported for FFI purposes).**
///
/// Gets the currently stored JWE as a JSON String of the serialized value.
///
/// This doesn't clear the stored value.
pub fn test_get_value_as_json_string(
&self,
glean: &Glean,
storage_name: &str,
) -> Option<String> {
self.test_get_value(glean, storage_name).map(|snapshot| {
serde_json::to_string(
&Jwe::from_str(&snapshot).expect("Stored JWE metric should be valid JWE value."),
)
.unwrap()
})
}
}
#[cfg(test)]
mod test {
use super::*;
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_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());
}
#[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),
}
// When header is bigger than max size, correct error is returned
let too_long = (0..1025).map(|_| "X").collect::<String>();
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),
}
// 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),
}
}
#[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::<String>();
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 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),
}
// 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),
}
}
#[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::<String>();
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),
}
// 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),
}
}
#[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]
fn tranforms_jwe_struct_to_string_correctly() {
let jwe = Jwe::from_str(JWE).unwrap();
assert_eq!(jwe.to_string(), JWE);
}
#[test]
fn validates_base64url_correctly() {
assert!(validate_base64url_encoding(
"0987654321AaBbCcDdEeFfGgHhIiKkLlMmNnOoPpQqRrSsTtUuVvXxWwYyZz-_"
));
assert!(validate_base64url_encoding(""));
assert!(!validate_base64url_encoding("aa aa"));
assert!(!validate_base64url_encoding("aa.aa"));
assert!(!validate_base64url_encoding("!nv@lid-val*e"));
}
}

Просмотреть файл

@ -17,7 +17,6 @@ mod datetime;
mod denominator;
mod event;
mod experiment;
mod jwe;
pub(crate) mod labeled;
mod memory_distribution;
mod memory_unit;
@ -47,7 +46,6 @@ pub use self::datetime::DatetimeMetric;
pub use self::denominator::DenominatorMetric;
pub use self::event::EventMetric;
pub(crate) use self::experiment::ExperimentMetric;
pub use self::jwe::JweMetric;
pub use self::labeled::{LabeledBoolean, LabeledCounter, LabeledMetric, LabeledString};
pub use self::memory_distribution::MemoryDistributionMetric;
pub use self::memory_unit::MemoryUnit;
@ -117,7 +115,9 @@ pub enum Metric {
TimingDistribution(Histogram<Functional>),
/// A memory distribution. See [`MemoryDistributionMetric`] for more information.
MemoryDistribution(Histogram<Functional>),
/// A JWE metric. See [`JweMetric`] for more information.
/// **DEPRECATED**: A JWE metric..
/// Note: This variant MUST NOT be removed to avoid backwards-incompatible changes to the
/// serialization. This type has no underlying implementation anymore.
Jwe(String),
/// A rate metric. See [`RateMetric`] for more information.
Rate(i32, i32),

Просмотреть файл

@ -1,54 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/// A description for the [`JweMetric`](crate::metrics::JweMetric) type.
///
/// When changing this trait, make sure all the operations are
/// implemented in the related type in `../metrics/`.
pub trait Jwe {
/// Sets to the specified JWE value.
///
/// # Arguments
///
/// * `value` - the [`compact representation`](https://tools.ietf.org/html/rfc7516#appendix-A.2.7) of a JWE value.
fn set_with_compact_representation<S: Into<String>>(&self, value: S);
/// Builds a JWE value from its elements and set to it.
///
/// # Arguments
///
/// * `header` - the JWE Protected Header element.
/// * `key` - the JWE Encrypted Key element.
/// * `init_vector` - the JWE Initialization Vector element.
/// * `cipher_text` - the JWE Ciphertext element.
/// * `auth_tag` - the JWE Authentication Tag element.
fn set<S: Into<String>>(&self, header: S, key: S, init_vector: S, cipher_text: S, auth_tag: S);
/// **Exported for test purposes.**
///
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
///
/// # Arguments
///
/// * `ping_name` - represents the optional name of the ping to retrieve the
/// metric for. Defaults to the first value in `send_in_pings`.
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<String>;
/// **Exported for test purposes.**
///
/// Gets the currently stored JWE as a JSON String of the serialized value.
///
/// This doesn't clear the stored value.
///
/// # Arguments
///
/// * `ping_name` - represents the optional name of the ping to retrieve the
/// metric for. Defaults to the first value in `send_in_pings`.
fn test_get_value_as_json_string<'a, S: Into<Option<&'a str>>>(
&self,
ping_name: S,
) -> Option<String>;
}

Просмотреть файл

@ -12,7 +12,6 @@ mod counter;
mod custom_distribution;
mod datetime;
mod event;
mod jwe;
mod labeled;
mod memory_distribution;
mod numerator;
@ -34,7 +33,6 @@ pub use self::event::Event;
pub use self::event::EventRecordingError;
pub use self::event::ExtraKeys;
pub use self::event::NoExtraKeys;
pub use self::jwe::Jwe;
pub use self::labeled::Labeled;
pub use self::memory_distribution::MemoryDistribution;
pub use self::numerator::Numerator;

Просмотреть файл

@ -1,113 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
mod common;
use crate::common::*;
use serde_json::json;
use glean_core::metrics::*;
use glean_core::storage::StorageManager;
use glean_core::{CommonMetricData, Lifetime};
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() {
let (glean, _t) = new_glean(None);
let metric = JweMetric::new(CommonMetricData {
name: "jwe_metric".into(),
category: "local".into(),
send_in_pings: vec!["core".into()],
..Default::default()
});
metric.set_with_compact_representation(&glean, JWE);
let snapshot = StorageManager
.snapshot_as_json(glean.storage(), "core", false)
.unwrap();
assert_eq!(
json!({"jwe": {"local.jwe_metric": metric.test_get_value(&glean, "core") }}),
snapshot
);
}
#[test]
fn set_properly_sets_the_value_in_all_stores() {
let (glean, _t) = new_glean(None);
let store_names: Vec<String> = vec!["store1".into(), "store2".into()];
let metric = JweMetric::new(CommonMetricData {
name: "jwe_metric".into(),
category: "local".into(),
send_in_pings: store_names.clone(),
disabled: false,
lifetime: Lifetime::Ping,
..Default::default()
});
metric.set_with_compact_representation(&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, false)
.unwrap();
assert_eq!(
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_representation(&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_representation(&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
);
}