Access token permissions

This commit is contained in:
mdecimus
2024-09-10 18:44:44 +02:00
parent 08a95ae58b
commit fbcf55d8e1
128 changed files with 2415 additions and 906 deletions

View File

@@ -7,6 +7,7 @@ resolver = "2"
[dependencies]
store = { path = "../store" }
common = { path = "../common" }
directory = { path = "../directory" }
jmap = { path = "../jmap" }
imap = { path = "../imap" }
utils = { path = "../utils" }

View File

@@ -9,7 +9,7 @@ use mail_send::Credentials;
use trc::AddContext;
use crate::{
protocol::{request::Error, response::Response, Command, Mechanism},
protocol::{request::Error, Command, Mechanism},
Session, State,
};
@@ -117,54 +117,11 @@ impl<T: SessionStream> Session<T> {
self.write_ok("NOOP").await.map(|_| SessionResult::Continue)
}
Command::Rset => self.handle_rset().await.map(|_| SessionResult::Continue),
Command::Capa => {
let mechanisms =
if self.stream.is_tls() || self.jmap.core.imap.allow_plain_auth {
vec![Mechanism::Plain, Mechanism::OAuthBearer]
} else {
vec![Mechanism::OAuthBearer]
};
trc::event!(
Pop3(trc::Pop3Event::Capabilities),
SpanId = self.session_id,
Tls = self.stream.is_tls(),
Strict = !self.jmap.core.imap.allow_plain_auth,
Elapsed = trc::Value::Duration(0)
);
self.write_bytes(
Response::Capability::<u32> {
mechanisms,
stls: !self.stream.is_tls(),
}
.serialize(),
)
.await
.map(|_| SessionResult::Continue)
}
Command::Capa => self.handle_capa().await.map(|_| SessionResult::Continue),
Command::Stls => {
trc::event!(
Pop3(trc::Pop3Event::StartTls),
SpanId = self.session_id,
Elapsed = trc::Value::Duration(0)
);
self.write_ok("Begin TLS negotiation now")
.await
.map(|_| SessionResult::UpgradeTls)
}
Command::Utf8 => {
trc::event!(
Pop3(trc::Pop3Event::Utf8),
SpanId = self.session_id,
Elapsed = trc::Value::Duration(0)
);
self.write_ok("UTF8 enabled")
.await
.map(|_| SessionResult::Continue)
self.handle_stls().await.map(|_| SessionResult::UpgradeTls)
}
Command::Utf8 => self.handle_utf8().await.map(|_| SessionResult::Continue),
Command::Auth { mechanism, params } => self
.handle_sasl(mechanism, params)
.await

View File

@@ -8,7 +8,7 @@ use std::{net::IpAddr, sync::Arc};
use common::listener::{limiter::InFlight, ServerInstance, SessionStream};
use imap::core::{ImapInstance, Inner};
use jmap::JMAP;
use jmap::{auth::AccessToken, JMAP};
use mailbox::Mailbox;
use protocol::request::Parser;
@@ -51,6 +51,7 @@ pub enum State {
Authenticated {
mailbox: Mailbox,
in_flight: Option<InFlight>,
access_token: Arc<AccessToken>,
},
}
@@ -68,4 +69,11 @@ impl State {
_ => unreachable!(),
}
}
pub fn access_token(&self) -> &Arc<AccessToken> {
match self {
State::Authenticated { access_token, .. } => access_token,
_ => unreachable!(),
}
}
}

View File

@@ -5,6 +5,7 @@
*/
use common::listener::{limiter::ConcurrencyLimiter, SessionStream};
use directory::Permission;
use imap::op::authenticate::{decode_challenge_oauth, decode_challenge_plain};
use jmap::auth::rate_limit::ConcurrencyLimiters;
use mail_parser::decoders::base64::base64_decode;
@@ -112,6 +113,9 @@ impl<T: SessionStream> Session<T> {
}
};
// Validate access
access_token.assert_has_permission(Permission::Pop3Authenticate)?;
// Cache access token
let access_token = Arc::new(access_token);
self.jmap.cache_access_token(access_token.clone());
@@ -120,7 +124,11 @@ impl<T: SessionStream> Session<T> {
let mailbox = self.fetch_mailbox(access_token.primary_id()).await?;
// Create session
self.state = State::Authenticated { in_flight, mailbox };
self.state = State::Authenticated {
in_flight,
mailbox,
access_token,
};
self.write_ok("Authentication successful").await
}

View File

@@ -7,6 +7,7 @@
use std::time::Instant;
use common::listener::SessionStream;
use directory::Permission;
use jmap_proto::types::{state::StateChange, type_state::DataType};
use store::roaring::RoaringBitmap;
use trc::AddContext;
@@ -15,6 +16,11 @@ use crate::{protocol::response::Response, Session, State};
impl<T: SessionStream> Session<T> {
pub async fn handle_dele(&mut self, msgs: Vec<u32>) -> trc::Result<()> {
// Validate access
self.state
.access_token()
.assert_has_permission(Permission::Pop3Dele)?;
let op_start = Instant::now();
let mailbox = self.state.mailbox_mut();
let mut response = Vec::new();

View File

@@ -7,6 +7,7 @@
use std::time::Instant;
use common::listener::SessionStream;
use directory::Permission;
use jmap::email::metadata::MessageMetadata;
use jmap_proto::types::{collection::Collection, property::Property};
use store::write::Bincode;
@@ -16,6 +17,11 @@ use crate::{protocol::response::Response, Session};
impl<T: SessionStream> Session<T> {
pub async fn handle_fetch(&mut self, msg: u32, lines: Option<u32>) -> trc::Result<()> {
// Validate access
self.state
.access_token()
.assert_has_permission(Permission::Pop3Retr)?;
let op_start = Instant::now();
let mailbox = self.state.mailbox();
if let Some(message) = mailbox.messages.get(msg.saturating_sub(1) as usize) {

View File

@@ -7,11 +7,17 @@
use std::time::Instant;
use common::listener::SessionStream;
use directory::Permission;
use crate::{protocol::response::Response, Session};
impl<T: SessionStream> Session<T> {
pub async fn handle_list(&mut self, msg: Option<u32>) -> trc::Result<()> {
// Validate access
self.state
.access_token()
.assert_has_permission(Permission::Pop3List)?;
let op_start = Instant::now();
let mailbox = self.state.mailbox();
if let Some(msg) = msg {
@@ -48,6 +54,11 @@ impl<T: SessionStream> Session<T> {
}
pub async fn handle_uidl(&mut self, msg: Option<u32>) -> trc::Result<()> {
// Validate access
self.state
.access_token()
.assert_has_permission(Permission::Pop3Uidl)?;
let op_start = Instant::now();
let mailbox = self.state.mailbox();
if let Some(msg) = msg {
@@ -92,6 +103,11 @@ impl<T: SessionStream> Session<T> {
}
pub async fn handle_stat(&mut self) -> trc::Result<()> {
// Validate access
self.state
.access_token()
.assert_has_permission(Permission::Pop3Stat)?;
let op_start = Instant::now();
let mailbox = self.state.mailbox();

View File

@@ -4,7 +4,61 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use common::listener::SessionStream;
use crate::{
protocol::{response::Response, Mechanism},
Session,
};
pub mod authenticate;
pub mod delete;
pub mod fetch;
pub mod list;
impl<T: SessionStream> Session<T> {
pub async fn handle_capa(&mut self) -> trc::Result<()> {
let mechanisms = if self.stream.is_tls() || self.jmap.core.imap.allow_plain_auth {
vec![Mechanism::Plain, Mechanism::OAuthBearer]
} else {
vec![Mechanism::OAuthBearer]
};
trc::event!(
Pop3(trc::Pop3Event::Capabilities),
SpanId = self.session_id,
Tls = self.stream.is_tls(),
Strict = !self.jmap.core.imap.allow_plain_auth,
Elapsed = trc::Value::Duration(0)
);
self.write_bytes(
Response::Capability::<u32> {
mechanisms,
stls: !self.stream.is_tls(),
}
.serialize(),
)
.await
}
pub async fn handle_stls(&mut self) -> trc::Result<()> {
trc::event!(
Pop3(trc::Pop3Event::StartTls),
SpanId = self.session_id,
Elapsed = trc::Value::Duration(0)
);
self.write_ok("Begin TLS negotiation now").await
}
pub async fn handle_utf8(&mut self) -> trc::Result<()> {
trc::event!(
Pop3(trc::Pop3Event::Utf8),
SpanId = self.session_id,
Elapsed = trc::Value::Duration(0)
);
self.write_ok("UTF8 enabled").await
}
}