Add session limit (#2714)

This commit is contained in:
Firstyear 2024-04-23 16:02:42 +10:00 committed by GitHub
parent 9354c180af
commit 604adccdae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 81 additions and 2 deletions

View file

@ -151,3 +151,6 @@ pub const DEFAULT_LIMIT_FILTER_MAX_ELEMENTS: u64 = 32;
/// The maximum amount of recursion allowed in a filter.
pub const DEFAULT_LIMIT_FILTER_DEPTH_MAX: u64 = 12;
/// The maximum number of sessions allowed on a single entry.
pub(crate) const SESSION_MAXIMUM: usize = 48;

View file

@ -425,7 +425,38 @@ impl ValueSetT for ValueSetSession {
// Retain all else
_ => true,
}
})
});
// Now, assert that there are fewer or equal sessions to the limit.
if self.map.len() > SESSION_MAXIMUM {
// At this point we will force a number of sessions to be removed. This
// is replication safe since other replicas will also be performing
// the same operation on merge, since we trim by session issuance order.
// This is a "slow path". This is becase we optimise session storage
// based on fast session lookup, so now we need to actually create an
// index based on time. We need to also clone here since we need to mutate
// self.map which would violate mut/imut.
warn!(
"entry has exceeded session_maximum limit ({:?}), force trimming will occur",
SESSION_MAXIMUM
);
let time_idx: BTreeMap<OffsetDateTime, Uuid> = self
.map
.iter()
.map(|(session_id, session)| (session.issued_at.clone(), *session_id))
.collect();
let to_take = self.map.len() - SESSION_MAXIMUM;
time_idx.values().take(to_take).for_each(|session_id| {
warn!(?session_id, "force trimmed");
self.map.remove(session_id);
});
}
// And we're done.
}
fn contains(&self, pv: &PartialValue) -> bool {
@ -1682,7 +1713,7 @@ impl ValueSetT for ValueSetApiToken {
#[cfg(test)]
mod tests {
use super::{ValueSetOauth2Session, ValueSetSession};
use super::{ValueSetOauth2Session, ValueSetSession, SESSION_MAXIMUM};
use crate::prelude::{IdentityId, SessionScope, Uuid};
use crate::repl::cid::Cid;
use crate::value::{Oauth2Session, Session, SessionState};
@ -1971,6 +2002,51 @@ mod tests {
assert!(sessions.contains_key(&two_uuid));
}
#[test]
fn test_valueset_session_limit_trim() {
// Create a session that will be trimmed.
let zero_uuid = Uuid::new_v4();
let zero_cid = Cid::new_zero();
let issued_at = OffsetDateTime::UNIX_EPOCH;
let session_iter = std::iter::once((
zero_uuid,
Session {
state: SessionState::NeverExpires,
label: "hacks".to_string(),
issued_at,
issued_by: IdentityId::Internal,
cred_id: Uuid::new_v4(),
scope: SessionScope::ReadOnly,
},
))
.chain((0..SESSION_MAXIMUM).into_iter().map(|_| {
(
Uuid::new_v4(),
Session {
state: SessionState::NeverExpires,
label: "hacks".to_string(),
issued_at: OffsetDateTime::now_utc(),
issued_by: IdentityId::Internal,
cred_id: Uuid::new_v4(),
scope: SessionScope::ReadOnly,
},
)
}));
let mut vs_a: ValueSet = ValueSetSession::from_iter(session_iter).unwrap();
assert!(vs_a.len() > SESSION_MAXIMUM);
vs_a.trim(&zero_cid);
assert!(vs_a.len() == SESSION_MAXIMUM);
let sessions = vs_a.as_session_map().expect("Unable to access sessions");
assert!(!sessions.contains_key(&zero_uuid));
}
#[test]
fn test_valueset_oauth2_session_purge() {
let s_uuid = Uuid::new_v4();