···8383#[cfg(test)]
8484mod tests;
85858686-use std::collections::HashMap;
8786use std::error::Error as StdError;
88878988use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine};
9089use chrono::{DateTime, Utc};
9190use displaydoc::Display;
9191+use indexmap::IndexMap;
9292pub use jwt_simple::{
9393 algorithms::{HS256Key, MACLike},
9494 claims::{Claims, JWTClaims},
···146146 /// Cache permissions.
147147 ///
148148 /// Keys here may include wildcards.
149149- caches: HashMap<CacheNamePattern, CachePermission>,
149149+ caches: IndexMap<CacheNamePattern, CachePermission>,
150150}
151151152152/// Permission to a single cache.
···274274 &mut self,
275275 pattern: CacheNamePattern,
276276 ) -> &mut CachePermission {
277277- use std::collections::hash_map::Entry;
277277+ use indexmap::map::Entry;
278278279279 let access = self.attic_access_mut();
280280 match access.caches.entry(pattern) {
+25-1
token/src/tests.rs
···2121 "exp": 4102324986,
2222 "https://jwt.attic.rs/v1": {
2323 "caches": {
2424+ "all-*": {"r":1},
2525+ "all-ci-*": {"w":1},
2426 "cache-rw": {"r":1,"w":1},
2527 "cache-ro": {"r":1},
2628 "team-*": {"r":1,"w":1,"cc":1}
···2931 }
3032 */
31333232- let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtZW93IiwiZXhwIjo0MTAyMzI0OTg2LCJodHRwczovL2p3dC5hdHRpYy5ycy92MSI6eyJjYWNoZXMiOnsiY2FjaGUtcnciOnsiciI6MSwidyI6MX0sImNhY2hlLXJvIjp7InIiOjF9LCJ0ZWFtLSoiOnsiciI6MSwidyI6MSwiY2MiOjF9fX19.UlsIM9bQHr9SXGAcSQcoVPo9No8Zhh6Y5xfX8vCmKmA";
3434+ let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjQxMDIzMjQ5ODYsImh0dHBzOi8vand0LmF0dGljLnJzL3YxIjp7ImNhY2hlcyI6eyJhbGwtKiI6eyJyIjoxfSwiYWxsLWNpLSoiOnsidyI6MX0sImNhY2hlLXJvIjp7InIiOjF9LCJjYWNoZS1ydyI6eyJyIjoxLCJ3IjoxfSwidGVhbS0qIjp7ImNjIjoxLCJyIjoxLCJ3IjoxfX19LCJpYXQiOjE3MTY2NjA1ODksInN1YiI6Im1lb3cifQ.8vtxp_1OEYdcnkGPM4c9ORXooJZV7DOTS4NRkMKN8mw";
3535+3636+ // NOTE(cole-h): check that we get a consistent iteration order when getting permissions for
3737+ // caches -- this depends on the order of the fields in the token, but should otherwise be
3838+ // consistent between iterations
3939+ let mut was_ever_wrong = false;
4040+ for _ in 0..=1_000 {
4141+ // NOTE(cole-h): we construct a new Token every iteration in order to get different "random
4242+ // state"
4343+ let decoded = Token::from_jwt(token, &dec_key).unwrap();
4444+ let perm_all_ci = decoded.get_permission_for_cache(&cache! { "all-ci-abc" });
4545+4646+ // NOTE(cole-h): if the iteration order of the token is inconsistent, the permissions may be
4747+ // retrieved from the `all-ci-*` pattern (which only allows writing/pushing), even though
4848+ // the `all-*` pattern (which only allows reading/pulling) is specified first
4949+ if perm_all_ci.require_pull().is_err() || perm_all_ci.require_push().is_ok() {
5050+ was_ever_wrong = true;
5151+ }
5252+ }
5353+ assert!(
5454+ !was_ever_wrong,
5555+ "Iteration order should be consistent to prevent random auth failures (and successes)"
5656+ );
33573458 let decoded = Token::from_jwt(token, &dec_key).unwrap();
3559