OAuth resource indicators: Accept imap, smtp, pop3 and sieve as valid resource indicators for OAuth access tokens
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -2,12 +2,23 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [0.16.13] - 2026-07-XX
|
||||||
|
|
||||||
|
If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- OAuth resource indicators: Accept `imap`, `smtp`, `pop3` and `sieve` as valid resource indicators for OAuth access tokens.
|
||||||
|
|
||||||
## [0.16.12] - 2026-07-06
|
## [0.16.12] - 2026-07-06
|
||||||
|
|
||||||
If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions.
|
If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions.
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
- DKIM2 implementation ([draft-ietf-dkim-dkim2-spec-03](https://datatracker.ietf.org/doc/draft-ietf-dkim-dkim2-spec/)).
|
- DKIM2 implementation ([draft-ietf-dkim-dkim2-spec-04](https://datatracker.ietf.org/doc/draft-ietf-dkim-dkim2-spec/)).
|
||||||
- DMARCbis implementation:
|
- DMARCbis implementation:
|
||||||
- Domain-based Message Authentication, Reporting, and Conformance (DMARC) ([RFC 9989](https://datatracker.ietf.org/doc/html/rfc9989))
|
- Domain-based Message Authentication, Reporting, and Conformance (DMARC) ([RFC 9989](https://datatracker.ietf.org/doc/html/rfc9989))
|
||||||
- DMARC Aggregate Reporting ([RFC 9990](https://datatracker.ietf.org/doc/html/rfc9990))
|
- DMARC Aggregate Reporting ([RFC 9990](https://datatracker.ietf.org/doc/html/rfc9990))
|
||||||
|
|||||||
@@ -225,10 +225,30 @@ impl OAuthApiHandler for Server {
|
|||||||
|
|
||||||
// Validate Resource Indicators (RFC 8707)
|
// Validate Resource Indicators (RFC 8707)
|
||||||
for resource in &resource {
|
for resource in &resource {
|
||||||
if !is_known_resource(&self.core.network.http.url_https, resource) {
|
if !is_known_resource(
|
||||||
|
[self.core.network.server_name.as_str()]
|
||||||
|
.into_iter()
|
||||||
|
.chain(
|
||||||
|
self.core
|
||||||
|
.network
|
||||||
|
.info
|
||||||
|
.services
|
||||||
|
.values()
|
||||||
|
.filter_map(|v| v.hostname.as_deref()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.core
|
||||||
|
.network
|
||||||
|
.info
|
||||||
|
.mxs
|
||||||
|
.iter()
|
||||||
|
.filter_map(|mx| mx.hostname.as_deref()),
|
||||||
|
),
|
||||||
|
resource,
|
||||||
|
) {
|
||||||
return Err(trc::AuthEvent::Error
|
return Err(trc::AuthEvent::Error
|
||||||
.into_err()
|
.into_err()
|
||||||
.details("Unknown resource indicator."));
|
.details(format!("Unknown resource indicator: {}", resource)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,8 +605,38 @@ fn grant_scope(requested: Option<&str>, registered_mask: u64) -> Option<String>
|
|||||||
(!granted.is_empty()).then_some(granted)
|
(!granted.is_empty()).then_some(granted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_known_resource(base_url: &str, uri: &str) -> bool {
|
fn is_known_resource<'x>(hostnames: impl IntoIterator<Item = &'x str>, uri: &str) -> bool {
|
||||||
let base = base_url.trim_end_matches('/');
|
let Some((scheme, rest)) = uri.split_once("://") else {
|
||||||
uri.strip_prefix(base)
|
return false;
|
||||||
.is_some_and(|rest| rest.is_empty() || rest.starts_with('/'))
|
};
|
||||||
|
let supported = hashify::tiny_map!(scheme.as_bytes(),
|
||||||
|
b"http" => true,
|
||||||
|
b"https" => true,
|
||||||
|
b"smtp" => true,
|
||||||
|
b"smtps" => true,
|
||||||
|
b"imap" => true,
|
||||||
|
b"imaps" => true,
|
||||||
|
b"pop3" => true,
|
||||||
|
b"pop3s" => true,
|
||||||
|
b"caldav" => true,
|
||||||
|
b"caldavs" => true,
|
||||||
|
b"webdav" => true,
|
||||||
|
b"webdavs" => true,
|
||||||
|
b"carddav" => true,
|
||||||
|
b"carddavs" => true,
|
||||||
|
b"sieve" => true,
|
||||||
|
b"sieves" => true
|
||||||
|
)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let authority = rest.split_once('/').map_or(rest, |(auth, _)| auth);
|
||||||
|
let host = authority
|
||||||
|
.rsplit_once(':')
|
||||||
|
.filter(|(h, _)| h.as_bytes().iter().all(|c| c.is_ascii_digit()))
|
||||||
|
.map_or(authority, |(h, _)| h);
|
||||||
|
|
||||||
|
supported
|
||||||
|
&& hostnames
|
||||||
|
.into_iter()
|
||||||
|
.any(|hostname| host.eq_ignore_ascii_case(hostname))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user