summaryrefslogtreecommitdiff
path: root/vendor/iri-string/src/parser/validate
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-15 16:37:08 -0600
committermo khan <mo@mokhan.ca>2025-07-17 16:30:22 -0600
commit45df4d0d9b577fecee798d672695fe24ff57fb1b (patch)
tree1b99bf645035b58e0d6db08c7a83521f41f7a75b /vendor/iri-string/src/parser/validate
parentf94f79608393d4ab127db63cc41668445ef6b243 (diff)
feat: migrate from Cedar to SpiceDB authorization system
This is a major architectural change that replaces the Cedar policy-based authorization system with SpiceDB's relation-based authorization. Key changes: - Migrate from Rust to Go implementation - Replace Cedar policies with SpiceDB schema and relationships - Switch from envoy `ext_authz` with Cedar to SpiceDB permission checks - Update build system and dependencies for Go ecosystem - Maintain Envoy integration for external authorization This change enables more flexible permission modeling through SpiceDB's Google Zanzibar inspired relation-based system, supporting complex hierarchical permissions that were difficult to express in Cedar. Breaking change: Existing Cedar policies and Rust-based configuration will no longer work and need to be migrated to SpiceDB schema.
Diffstat (limited to 'vendor/iri-string/src/parser/validate')
-rw-r--r--vendor/iri-string/src/parser/validate/authority.rs296
-rw-r--r--vendor/iri-string/src/parser/validate/path.rs91
2 files changed, 0 insertions, 387 deletions
diff --git a/vendor/iri-string/src/parser/validate/authority.rs b/vendor/iri-string/src/parser/validate/authority.rs
deleted file mode 100644
index fb41085e..00000000
--- a/vendor/iri-string/src/parser/validate/authority.rs
+++ /dev/null
@@ -1,296 +0,0 @@
-//! Parsers for authority.
-
-use core::mem;
-
-use crate::parser::char;
-use crate::parser::str::{
- find_split_hole, get_wrapped_inner, rfind_split_hole, satisfy_chars_with_pct_encoded,
- strip_ascii_char_prefix,
-};
-use crate::spec::Spec;
-use crate::validate::Error;
-
-/// Returns `Ok(_)` if the string matches `userinfo` or `iuserinfo`.
-pub(crate) fn validate_userinfo<S: Spec>(i: &str) -> Result<(), Error> {
- let is_valid = satisfy_chars_with_pct_encoded(
- i,
- char::is_ascii_userinfo_ipvfutureaddr,
- char::is_nonascii_userinfo::<S>,
- );
- if is_valid {
- Ok(())
- } else {
- Err(Error::new())
- }
-}
-
-/// Returns `true` if the string matches `dec-octet`.
-///
-/// In other words, this tests whether the string is decimal "0" to "255".
-#[must_use]
-fn is_dec_octet(i: &str) -> bool {
- matches!(
- i.as_bytes(),
- [b'0'..=b'9']
- | [b'1'..=b'9', b'0'..=b'9']
- | [b'1', b'0'..=b'9', b'0'..=b'9']
- | [b'2', b'0'..=b'4', b'0'..=b'9']
- | [b'2', b'5', b'0'..=b'5']
- )
-}
-
-/// Returns `Ok(_)` if the string matches `IPv4address`.
-fn validate_ipv4address(i: &str) -> Result<(), Error> {
- let (first, rest) = find_split_hole(i, b'.').ok_or_else(Error::new)?;
- if !is_dec_octet(first) {
- return Err(Error::new());
- }
- let (second, rest) = find_split_hole(rest, b'.').ok_or_else(Error::new)?;
- if !is_dec_octet(second) {
- return Err(Error::new());
- }
- let (third, fourth) = find_split_hole(rest, b'.').ok_or_else(Error::new)?;
- if is_dec_octet(third) && is_dec_octet(fourth) {
- Ok(())
- } else {
- Err(Error::new())
- }
-}
-
-/// A part of IPv6 addr.
-#[derive(Clone, Copy)]
-enum V6AddrPart {
- /// `[0-9a-fA-F]{1,4}::`.
- H16Omit,
- /// `[0-9a-fA-F]{1,4}:`.
- H16Cont,
- /// `[0-9a-fA-F]{1,4}`.
- H16End,
- /// IPv4 address.
- V4,
- /// `::`.
- Omit,
-}
-
-/// Splits the IPv6 address string into the next component and the rest substring.
-fn split_v6_addr_part(i: &str) -> Result<(&str, V6AddrPart), Error> {
- debug_assert!(!i.is_empty());
- match find_split_hole(i, b':') {
- Some((prefix, rest)) => {
- if prefix.len() >= 5 {
- return Err(Error::new());
- }
-
- if prefix.is_empty() {
- return match strip_ascii_char_prefix(rest, b':') {
- Some(rest) => Ok((rest, V6AddrPart::Omit)),
- None => Err(Error::new()),
- };
- }
-
- // Should be `h16`.
- debug_assert!((1..=4).contains(&prefix.len()));
- if !prefix.bytes().all(|b| b.is_ascii_hexdigit()) {
- return Err(Error::new());
- }
- match strip_ascii_char_prefix(rest, b':') {
- Some(rest) => Ok((rest, V6AddrPart::H16Omit)),
- None => Ok((rest, V6AddrPart::H16Cont)),
- }
- }
- None => {
- if i.len() >= 5 {
- // Possibly `IPv4address`.
- validate_ipv4address(i)?;
- return Ok(("", V6AddrPart::V4));
- }
- if i.bytes().all(|b| b.is_ascii_hexdigit()) {
- Ok(("", V6AddrPart::H16End))
- } else {
- Err(Error::new())
- }
- }
- }
-}
-
-/// Returns `Ok(_)` if the string matches `IPv6address`.
-fn validate_ipv6address(mut i: &str) -> Result<(), Error> {
- let mut h16_count = 0;
- let mut is_omitted = false;
- while !i.is_empty() {
- let (rest, part) = split_v6_addr_part(i)?;
- match part {
- V6AddrPart::H16Omit => {
- h16_count += 1;
- if mem::replace(&mut is_omitted, true) {
- // Omitted twice.
- return Err(Error::new());
- }
- }
- V6AddrPart::H16Cont => {
- h16_count += 1;
- if rest.is_empty() {
- // `H16Cont` cannot be the last part of an IPv6 address.
- return Err(Error::new());
- }
- }
- V6AddrPart::H16End => {
- h16_count += 1;
- break;
- }
- V6AddrPart::V4 => {
- debug_assert!(rest.is_empty());
- h16_count += 2;
- break;
- }
- V6AddrPart::Omit => {
- if mem::replace(&mut is_omitted, true) {
- // Omitted twice.
- return Err(Error::new());
- }
- }
- }
- if h16_count > 8 {
- return Err(Error::new());
- }
- i = rest;
- }
- let is_valid = if is_omitted {
- h16_count < 8
- } else {
- h16_count == 8
- };
- if is_valid {
- Ok(())
- } else {
- Err(Error::new())
- }
-}
-
-/// Returns `Ok(_)` if the string matches `authority` or `iauthority`.
-pub(super) fn validate_authority<S: Spec>(i: &str) -> Result<(), Error> {
- // Strip and validate `userinfo`.
- let (i, _userinfo) = match find_split_hole(i, b'@') {
- Some((maybe_userinfo, i)) => {
- validate_userinfo::<S>(maybe_userinfo)?;
- (i, Some(maybe_userinfo))
- }
- None => (i, None),
- };
- // `host` can contain colons, but `port` cannot.
- // Strip and validate `port`.
- let (maybe_host, _port) = match rfind_split_hole(i, b':') {
- Some((maybe_host, maybe_port)) => {
- if maybe_port.bytes().all(|b| b.is_ascii_digit()) {
- (maybe_host, Some(maybe_port))
- } else {
- (i, None)
- }
- }
- None => (i, None),
- };
- // Validate `host`.
- validate_host::<S>(maybe_host)
-}
-
-/// Validates `host`.
-pub(crate) fn validate_host<S: Spec>(i: &str) -> Result<(), Error> {
- match get_wrapped_inner(i, b'[', b']') {
- Some(maybe_addr) => {
- // `IP-literal`.
- // Note that `v` here is case insensitive. See RFC 3987 section 3.2.2.
- if let Some(maybe_addr_rest) = strip_ascii_char_prefix(maybe_addr, b'v')
- .or_else(|| strip_ascii_char_prefix(maybe_addr, b'V'))
- {
- // `IPvFuture`.
- let (maybe_ver, maybe_addr) =
- find_split_hole(maybe_addr_rest, b'.').ok_or_else(Error::new)?;
- // Validate version.
- if maybe_ver.is_empty() || !maybe_ver.bytes().all(|b| b.is_ascii_hexdigit()) {
- return Err(Error::new());
- }
- // Validate address.
- if !maybe_addr.is_empty()
- && maybe_addr.is_ascii()
- && maybe_addr
- .bytes()
- .all(char::is_ascii_userinfo_ipvfutureaddr)
- {
- Ok(())
- } else {
- Err(Error::new())
- }
- } else {
- // `IPv6address`.
- validate_ipv6address(maybe_addr)
- }
- }
- None => {
- // `IPv4address` or `reg-name`. No need to distinguish them here.
- let is_valid = satisfy_chars_with_pct_encoded(
- i,
- char::is_ascii_regname,
- char::is_nonascii_regname::<S>,
- );
- if is_valid {
- Ok(())
- } else {
- Err(Error::new())
- }
- }
- }
-}
-
-#[cfg(test)]
-#[cfg(feature = "alloc")]
-mod tests {
- use super::*;
-
- use alloc::format;
-
- macro_rules! assert_validate {
- ($parser:expr, $($input:expr),* $(,)?) => {{
- $({
- let input = $input;
- let input: &str = input.as_ref();
- assert!($parser(input).is_ok(), "input={:?}", input);
- })*
- }};
- }
-
- #[test]
- fn test_ipv6address() {
- use core::cmp::Ordering;
-
- assert_validate!(validate_ipv6address, "a:bB:cCc:dDdD:e:F:a:B");
- assert_validate!(validate_ipv6address, "1:1:1:1:1:1:1:1");
- assert_validate!(validate_ipv6address, "1:1:1:1:1:1:1.1.1.1");
- assert_validate!(validate_ipv6address, "2001:db8::7");
-
- // Generate IPv6 addresses with `::`.
- let make_sub = |n: usize| {
- let mut s = "1:".repeat(n);
- s.pop();
- s
- };
- for len_pref in 0..=7 {
- let prefix = make_sub(len_pref);
- for len_suf in 1..=(7 - len_pref) {
- assert_validate!(
- validate_ipv6address,
- &format!("{}::{}", prefix, make_sub(len_suf))
- );
- match len_suf.cmp(&2) {
- Ordering::Greater => assert_validate!(
- validate_ipv6address,
- &format!("{}::{}:1.1.1.1", prefix, make_sub(len_suf - 2))
- ),
- Ordering::Equal => {
- assert_validate!(validate_ipv6address, &format!("{}::1.1.1.1", prefix))
- }
- Ordering::Less => {}
- }
- }
- }
- }
-}
diff --git a/vendor/iri-string/src/parser/validate/path.rs b/vendor/iri-string/src/parser/validate/path.rs
deleted file mode 100644
index 1b09c84b..00000000
--- a/vendor/iri-string/src/parser/validate/path.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-//! Parsers for path.
-
-use crate::parser::char;
-use crate::parser::str::{find_split2_hole, satisfy_chars_with_pct_encoded};
-use crate::spec::Spec;
-use crate::validate::Error;
-
-/// Returns `Ok(_)` if the string matches `path-abempty` or `ipath-abempty`.
-pub(super) fn validate_path_abempty<S: Spec>(i: &str) -> Result<(), Error> {
- if i.is_empty() {
- return Ok(());
- }
- let i = match i.strip_prefix('/') {
- Some(rest) => rest,
- None => return Err(Error::new()),
- };
- let is_valid = satisfy_chars_with_pct_encoded(
- i,
- char::is_ascii_pchar_slash,
- S::is_nonascii_char_unreserved,
- );
- if is_valid {
- Ok(())
- } else {
- Err(Error::new())
- }
-}
-
-/// Returns `Ok(_)` if the string matches `hier-part` or `ihier-part` modulo
-/// `"//" authority path-abempty`.
-pub(super) fn validate_path_absolute_authority_absent<S: Spec>(i: &str) -> Result<(), Error> {
- if i.is_empty() {
- // `path-empty`.
- return Ok(());
- }
- if i.starts_with("//") {
- unreachable!("this case should be handled by the caller");
- }
- let is_valid = satisfy_chars_with_pct_encoded(
- i,
- char::is_ascii_pchar_slash,
- S::is_nonascii_char_unreserved,
- );
- if is_valid {
- Ok(())
- } else {
- Err(Error::new())
- }
-}
-
-/// Returns `Ok(_)` if the string matches `relative-part` or `irelative-part` modulo
-/// `"//" authority path-abempty`.
-pub(super) fn validate_path_relative_authority_absent<S: Spec>(i: &str) -> Result<(), Error> {
- if i.starts_with("//") {
- unreachable!("this case should be handled by the caller");
- }
- let is_valid = match find_split2_hole(i, b'/', b':') {
- Some((_, b'/', _)) | None => satisfy_chars_with_pct_encoded(
- i,
- char::is_ascii_pchar_slash,
- S::is_nonascii_char_unreserved,
- ),
- Some((_, c, _)) => {
- debug_assert_eq!(c, b':');
- // `foo:bar`-style. This does not match `path-noscheme`.
- return Err(Error::new());
- }
- };
- if is_valid {
- Ok(())
- } else {
- Err(Error::new())
- }
-}
-
-/// Returns `Ok(_)` if the string matches `path`/`ipath` rules.
-pub(crate) fn validate_path<S: Spec>(i: &str) -> Result<(), Error> {
- if i.starts_with("//") {
- return Err(Error::new());
- }
- let is_valid = satisfy_chars_with_pct_encoded(
- i,
- char::is_ascii_pchar_slash,
- S::is_nonascii_char_unreserved,
- );
- if is_valid {
- Ok(())
- } else {
- Err(Error::new())
- }
-}