зеркало из https://github.com/mozilla/glean.git
Remove already-deprecated JWE metric.
It's already disabled in the glean_parser and thus cannot be used anyway.
This commit is contained in:
Родитель
d32bb60065
Коммит
231a1e50b1
|
@ -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
|
||||
);
|
||||
}
|
Загрузка…
Ссылка в новой задаче