Skip to main content

Dispatch

Struct Dispatch 

Source
pub struct Dispatch { /* private fields */ }
Expand description

Parse → resolve → check, with no SSH anywhere in it.

Kept separate from the Handler so the security decision can be tested directly, exhaustively and without a handshake, and so there is exactly one place where it is made.

Implementations§

Source§

impl Dispatch

Source

pub fn new(control: Control) -> Self

Build a dispatch over the control plane.

Source

pub fn control(&self) -> &Control

The control plane this dispatch authenticates and authorises against.

Source

pub fn authorise( &self, principal: &Principal, key_fingerprint: Option<&Fingerprint>, raw: &[u8], ) -> Result<ServiceRequest, DispatchError>

Decide whether principal may run raw on this server.

Parse (which includes the sandbox), resolve, then check the grant. A storage failure at either lookup comes back as DispatchError::Store rather than as a denial. See crate::control.

The client’s environment is not a parameter here, and must not become one. The returned ServiceRequest carries git_protocol: None; the handler attaches the allowlisted value with ServiceRequest::with_git_protocol after this decision has already been made, so there is no path by which anything the client typed into its environment can reach the grant check.

Source

pub fn authorise_service( &self, principal: &Principal, key_fingerprint: Option<&Fingerprint>, service: GitService, path: RepoRef, ) -> Result<ServiceRequest, DispatchError>

The same decision, entered from a URL path rather than an exec line.

Smart HTTP does not send git-upload-pack '<path>'; it sends POST /<path>/git-upload-pack. The verb and the path arrive already separated, and the path has no shell quoting on it, so there is nothing for command::parse to do — but everything after the parse is identical and must stay identical: resolve, ask the ACL, and refuse in the one way that does not tell a caller whether a repository exists.

So this is where that decision lives and Dispatch::authorise is now a parser in front of it. Two entry points, one answer (one implementation). A second copy of this in gunnar-http is precisely the thing that ends with SSH and HTTP disagreeing about who may push, and the disagreement being found by whoever pushes.

repo is a RepoRef, which is to say it has already been through gunnar_core::resolve_repo_path. The sandbox is not optional on this path either: the caller cannot construct a RepoRef without it.

Trait Implementations§

Source§

impl Clone for Dispatch

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Dispatch

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more