Skip to content

Commit c74aea5

Browse files
committed
Stop sessions owned by revoked identities
1 parent d6980b1 commit c74aea5

3 files changed

Lines changed: 56 additions & 30 deletions

File tree

common/src/jobs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,11 @@ pub enum JobStatus {
243243
}
244244

245245
impl JobStatus {
246-
pub fn session_id(&self) -> Option<SessionId> {
246+
pub fn session_id(&self) -> Option<&SessionId> {
247247
match self {
248248
Self::Unknown { .. } => None,
249-
Self::Started { session_id, .. } => Some(session_id.to_owned()),
250-
Self::Ended { session_id, .. } => Some(session_id.to_owned()),
249+
Self::Started { session_id, .. } => Some(session_id),
250+
Self::Ended { session_id, .. } => Some(session_id),
251251
}
252252
}
253253

server/src/error.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ pub enum JobError {
8181
Task(#[from] tokio::task::JoinError),
8282
#[error("Too many certificates ({0})")]
8383
TooManyCerts(usize),
84-
#[error("Too many identities ({0}), try waiting for some to expire")]
85-
TooManyIdentities(usize),
8684
#[error("Too many jobs in a session ({0}), try waiting for some to finish")]
8785
TooManyJobs(usize),
86+
#[error("Too many identities revoked ({0})")]
87+
TooManyRevocations(usize),
8888
#[error("Unauthorized request")]
8989
Unauthorized(Nonce),
9090
#[error("Unable to wait for job end")]
@@ -185,8 +185,8 @@ impl From<JobError> for HttpError {
185185
| SessionWrongIdentity
186186
| OutputPending
187187
| TooManyCerts(_)
188-
| TooManyIdentities(_)
189-
| TooManyJobs(_) => {
188+
| TooManyJobs(_)
189+
| TooManyRevocations(_) => {
190190
HttpError::for_client_error(None, ClientErrorStatusCode::BAD_REQUEST, message)
191191
}
192192
}

server/src/manager.rs

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,11 @@ const MAX_CERT_CHAIN_LEN: usize = 10;
5757
/// The ideal number of certs is 1, so this need not be large.
5858
const MAX_CERTS: usize = 100;
5959

60-
/// Maximum number of cached/revoked identities.
61-
const MAX_IDENTITIES: usize = 1_000;
60+
/// Maximum number of cached identities.
61+
const MAX_CACHED_IDENTITIES: NonZeroUsize = NonZeroUsize::new(1_000).unwrap();
62+
63+
/// Maximum number of revoked identities.
64+
const MAX_REVOKED_IDENTITIES: usize = 1_000;
6265

6366
/// Maximum number of active jobs in a session.
6467
const MAX_ACTIVE_JOBS: usize = 1_000;
@@ -82,7 +85,8 @@ type SessionGuard<'a> = MutexGuard<'a, Option<Session>>;
8285
pub struct JobManager {
8386
log: Logger,
8487
nonces: Arc<Mutex<LruCache<Nonce, DateTime<Utc>>>>,
85-
identities: Arc<Mutex<BTreeMap<KeyId, (Identity, Credentials)>>>,
88+
identities: Arc<Mutex<LruCache<KeyId, (Identity, Credentials)>>>,
89+
revoked_identities: Arc<Mutex<BTreeMap<KeyId, DateTime<Utc>>>>,
8690
certs: Arc<Mutex<BTreeMap<KeyId, Certificate>>>,
8791
session: Arc<Mutex<Option<Session>>>,
8892
active_jobs: Arc<Mutex<BTreeMap<JobId, JobStatus>>>,
@@ -124,7 +128,8 @@ impl JobManager {
124128
let new = Self {
125129
log: log.new(o!("component" => "manager")),
126130
nonces: Arc::new(Mutex::new(LruCache::new(MAX_OUTSTANDING_NONCES))),
127-
identities: Arc::new(Mutex::new(BTreeMap::new())),
131+
identities: Arc::new(Mutex::new(LruCache::new(MAX_CACHED_IDENTITIES))),
132+
revoked_identities: Arc::new(Mutex::new(BTreeMap::new())),
128133
certs: Arc::new(Mutex::new(BTreeMap::new())),
129134
session: Arc::new(Mutex::new(None)),
130135
active_jobs,
@@ -259,11 +264,21 @@ impl JobManager {
259264
signature,
260265
} = credentials.clone();
261266

267+
// Check for a revoked identity.
268+
if self
269+
.revoked_identities
270+
.lock()
271+
.unwrap()
272+
.contains_key(&key_id)
273+
{
274+
unauthorized!("identity revoked");
275+
}
276+
262277
// Check the cache.
263278
let mut identities = self.identities.lock().unwrap();
264279
let now = Utc::now();
265-
identities.retain(|_k, (i, _c)| i.is_still_valid(&now) || i.time_revoked.is_some());
266280
if let Some((identity, cached_credentials)) = identities.get(&key_id).cloned()
281+
&& try_authn!(identity.is_still_valid(&now), "identity expired")
267282
&& try_authn!(identity.time_revoked.is_none(), "identity revoked")
268283
&& cached_credentials.nonce == nonce
269284
&& cached_credentials.cnonce == cnonce
@@ -294,10 +309,7 @@ impl JobManager {
294309

295310
// Authenticated! Try to cache the credentials.
296311
debug!(self.log, "authenticated credentials for new identity"; "key_id" => %key_id);
297-
if identities.len() >= MAX_IDENTITIES {
298-
return Err(JobError::TooManyIdentities(MAX_IDENTITIES));
299-
}
300-
identities.insert(key_id.to_owned(), (identity.clone(), credentials));
312+
identities.put(key_id.to_owned(), (identity.clone(), credentials));
301313
Ok(identity)
302314
}
303315

@@ -312,15 +324,21 @@ impl JobManager {
312324
}
313325

314326
pub async fn revoke_identity(&self, _authn: &Identity, key_id: KeyId) -> Result<(), JobError> {
315-
let time_revoked = Utc::now();
316-
if let Some((identity, _credentials)) = self.identities.lock().unwrap().get_mut(&key_id) {
317-
identity.time_revoked = Some(time_revoked);
318-
} else {
319-
return Err(JobError::IdentityNotFound(key_id));
327+
if let Some(session) = {
328+
self.session
329+
.lock()
330+
.unwrap()
331+
.take_if(|session| session.key_id() == Some(&key_id))
332+
} {
333+
self.session_stop_inner(session).await?;
334+
}
335+
336+
let mut revoked = self.revoked_identities.lock().unwrap();
337+
if revoked.len() >= MAX_REVOKED_IDENTITIES {
338+
return Err(JobError::TooManyRevocations(MAX_REVOKED_IDENTITIES));
320339
}
321-
// for job_id in todo!("get interactive sessions") {
322-
// self.monitor(MonitorRequest::Stop(job_id)).await?;
323-
// }
340+
revoked.insert(key_id.clone(), Utc::now());
341+
324342
Ok(())
325343
}
326344

@@ -347,11 +365,7 @@ impl JobManager {
347365
// Anyone is allowed to stop a session.
348366
let session = {
349367
let mut session_guard = self.session.lock().unwrap();
350-
if session_guard.as_ref().map(|s| s.session_id()) == Some(session_id) {
351-
session_guard.take()
352-
} else {
353-
None
354-
}
368+
session_guard.take_if(|session| session.session_id() == session_id)
355369
};
356370
if let Some(session) = session {
357371
self.session_stop_inner(session).await
@@ -361,7 +375,18 @@ impl JobManager {
361375
}
362376

363377
async fn session_stop_inner(&self, session: Session) -> Result<(), JobError> {
364-
// TODO: send session stop event
378+
for job_id in {
379+
self.active_jobs
380+
.lock()
381+
.unwrap()
382+
.iter()
383+
.filter(|(_id, status)| status.session_id() == Some(session.session_id()))
384+
.map(|(id, _status)| id.to_owned())
385+
.collect::<Vec<JobId>>()
386+
} {
387+
self.monitor(MonitorRequest::Stop(job_id.to_owned()))
388+
.await?;
389+
}
365390
info!(self.log, "session stopped"; "session_id" => %session.session_id());
366391
Ok(())
367392
}
@@ -455,6 +480,7 @@ impl JobManager {
455480
let Some(job_session_id) = self
456481
.job_status_inner(&mut session_guard, job_id)?
457482
.session_id()
483+
.cloned()
458484
else {
459485
return Err(JobError::JobNotFound(job_id.to_owned()));
460486
};

0 commit comments

Comments
 (0)