diff options
Diffstat (limited to 'vendor/gix/src/remote/url/scheme_permission.rs')
-rw-r--r-- | vendor/gix/src/remote/url/scheme_permission.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/vendor/gix/src/remote/url/scheme_permission.rs b/vendor/gix/src/remote/url/scheme_permission.rs new file mode 100644 index 000000000..ddb87e111 --- /dev/null +++ b/vendor/gix/src/remote/url/scheme_permission.rs @@ -0,0 +1,120 @@ +use std::{borrow::Cow, collections::BTreeMap, convert::TryFrom}; + +use crate::{ + bstr::{BStr, BString, ByteSlice}, + config, + config::tree::{gitoxide, Key, Protocol}, +}; + +/// All allowed values of the `protocol.allow` key. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Allow { + /// Allow use this protocol. + Always, + /// Forbid using this protocol + Never, + /// Only supported if the `GIT_PROTOCOL_FROM_USER` is unset or is set to `1`. + User, +} + +impl Allow { + /// Return true if we represent something like 'allow == true'. + pub fn to_bool(self, user_allowed: Option<bool>) -> bool { + match self { + Allow::Always => true, + Allow::Never => false, + Allow::User => user_allowed.unwrap_or(true), + } + } +} + +impl<'a> TryFrom<Cow<'a, BStr>> for Allow { + type Error = BString; + + fn try_from(v: Cow<'a, BStr>) -> Result<Self, Self::Error> { + Ok(match v.as_ref().as_bytes() { + b"never" => Allow::Never, + b"always" => Allow::Always, + b"user" => Allow::User, + unknown => return Err(unknown.into()), + }) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct SchemePermission { + /// `None`, env-var is unset or wasn't queried, otherwise true if `GIT_PROTOCOL_FROM_USER` is `1`. + user_allowed: Option<bool>, + /// The general allow value from `protocol.allow`. + allow: Option<Allow>, + /// Per scheme allow information + allow_per_scheme: BTreeMap<gix_url::Scheme, Allow>, +} + +/// Init +impl SchemePermission { + /// NOTE: _intentionally without leniency_ + pub fn from_config( + config: &gix_config::File<'static>, + mut filter: fn(&gix_config::file::Metadata) -> bool, + ) -> Result<Self, config::protocol::allow::Error> { + let allow: Option<Allow> = config + .string_filter_by_key("protocol.allow", &mut filter) + .map(|value| Protocol::ALLOW.try_into_allow(value, None)) + .transpose()?; + + let mut saw_user = allow.map_or(false, |allow| allow == Allow::User); + let allow_per_scheme = match config.sections_by_name_and_filter("protocol", &mut filter) { + Some(it) => { + let mut map = BTreeMap::default(); + for (section, scheme) in it.filter_map(|section| { + section.header().subsection_name().and_then(|scheme| { + scheme + .to_str() + .ok() + .and_then(|scheme| gix_url::Scheme::try_from(scheme).ok().map(|scheme| (section, scheme))) + }) + }) { + if let Some(value) = section + .value("allow") + .map(|value| Protocol::ALLOW.try_into_allow(value, Some(scheme.as_str()))) + .transpose()? + { + saw_user |= value == Allow::User; + map.insert(scheme, value); + } + } + map + } + None => Default::default(), + }; + + let user_allowed = saw_user.then(|| { + config + .string_filter_by_key(gitoxide::Allow::PROTOCOL_FROM_USER.logical_name().as_str(), &mut filter) + .map_or(true, |val| val.as_ref() == "1") + }); + Ok(SchemePermission { + allow, + allow_per_scheme, + user_allowed, + }) + } +} + +/// Access +impl SchemePermission { + pub fn allow(&self, scheme: &gix_url::Scheme) -> bool { + self.allow_per_scheme.get(scheme).or(self.allow.as_ref()).map_or_else( + || { + use gix_url::Scheme::*; + match scheme { + File | Git | Ssh | Http | Https => true, + Ext(_) => false, + // TODO: figure out what 'ext' really entails, and what 'other' protocols are which aren't representable for us yet + } + }, + |allow| allow.to_bool(self.user_allowed), + ) + } +} |