token: switch to using IndexMap for consistent ordering of entries

Because of the random ordering of HashMap, if you have
overlapping token permissions, it is possible to randomly pick
one that leads to some operation working intermittently (see
https://github.com/zhaofengli/attic/issues/133 for an example of this).

By using an IndexMap instead, we make "iteration order of the key-value
pairs [...] independent of the hash values of the keys" (from the
indexmap crate docs [1]), which leads to more predictable behavior.

[1]: https://docs.rs/indexmap/latest/indexmap/
This commit is contained in:
Cole Helbling 2024-05-25 11:00:43 -07:00
parent f18f581188
commit 18dedcc30b
3 changed files with 11 additions and 9 deletions

13
Cargo.lock generated
View file

@ -351,6 +351,7 @@ dependencies = [
"base64 0.21.5", "base64 0.21.5",
"chrono", "chrono",
"displaydoc", "displaydoc",
"indexmap 2.2.6",
"jwt-simple", "jwt-simple",
"lazy_static", "lazy_static",
"regex", "regex",
@ -2204,9 +2205,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.1.0" version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.2", "hashbrown 0.14.2",
@ -3680,7 +3681,7 @@ dependencies = [
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.1.0", "indexmap 2.2.6",
"serde", "serde",
"serde_json", "serde_json",
"serde_with_macros", "serde_with_macros",
@ -3705,7 +3706,7 @@ version = "0.9.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c"
dependencies = [ dependencies = [
"indexmap 2.1.0", "indexmap 2.2.6",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
@ -3912,7 +3913,7 @@ dependencies = [
"futures-util", "futures-util",
"hashlink", "hashlink",
"hex", "hex",
"indexmap 2.1.0", "indexmap 2.2.6",
"log", "log",
"memchr", "memchr",
"once_cell", "once_cell",
@ -4404,7 +4405,7 @@ version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [ dependencies = [
"indexmap 2.1.0", "indexmap 2.2.6",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",

View file

@ -11,6 +11,7 @@ attic = { path = "../attic", default-features = false }
base64 = "0.21.2" base64 = "0.21.2"
chrono = "0.4.24" chrono = "0.4.24"
displaydoc = "0.2.4" displaydoc = "0.2.4"
indexmap = { version = "2.2.6", features = ["serde"] }
jwt-simple = "0.11.5" jwt-simple = "0.11.5"
lazy_static = "1.4.0" lazy_static = "1.4.0"
regex = "1.8.3" regex = "1.8.3"

View file

@ -83,12 +83,12 @@ pub mod util;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use std::collections::HashMap;
use std::error::Error as StdError; use std::error::Error as StdError;
use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine}; use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use displaydoc::Display; use displaydoc::Display;
use indexmap::IndexMap;
pub use jwt_simple::{ pub use jwt_simple::{
algorithms::{HS256Key, MACLike}, algorithms::{HS256Key, MACLike},
claims::{Claims, JWTClaims}, claims::{Claims, JWTClaims},
@ -146,7 +146,7 @@ pub struct AtticAccess {
/// Cache permissions. /// Cache permissions.
/// ///
/// Keys here may include wildcards. /// Keys here may include wildcards.
caches: HashMap<CacheNamePattern, CachePermission>, caches: IndexMap<CacheNamePattern, CachePermission>,
} }
/// Permission to a single cache. /// Permission to a single cache.
@ -274,7 +274,7 @@ impl Token {
&mut self, &mut self,
pattern: CacheNamePattern, pattern: CacheNamePattern,
) -> &mut CachePermission { ) -> &mut CachePermission {
use std::collections::hash_map::Entry; use indexmap::map::Entry;
let access = self.attic_access_mut(); let access = self.attic_access_mut();
match access.caches.entry(pattern) { match access.caches.entry(pattern) {