1use std::{fmt, mem};
3use std::borrow::Cow;
4
5use chrono::Utc;
6
7use crate::primitives::issuer::Issuer;
8use crate::primitives::grant::Grant;
9use crate::primitives::scope::Scope;
10
11#[derive(Clone, Debug)]
18pub struct AccessFailure {
19 pub code: Option<ErrorCode>,
21}
22
23#[derive(Debug, Clone, Copy)]
25pub enum ErrorCode {
26 InvalidRequest,
28
29 InsufficientScope,
31
32 InvalidToken,
34}
35
36#[derive(Clone, Debug)]
38pub struct Authenticate {
39 pub realm: Option<String>,
41
42 pub scope: Option<Scope>,
44}
45
46#[derive(Clone, Debug)]
48pub enum Error {
49 AccessDenied {
51 failure: AccessFailure,
53
54 authenticate: Authenticate,
56 },
57
58 NoAuthentication {
60 authenticate: Authenticate,
62 },
63
64 InvalidRequest {
66 authenticate: Authenticate,
68 },
69
70 PrimitiveError,
72}
73
74const BEARER_START: &str = "Bearer ";
75
76type Result<T> = std::result::Result<T, Error>;
77
78pub trait Request {
80 fn valid(&self) -> bool;
85
86 fn token(&self) -> Option<Cow<str>>;
92}
93
94pub trait Endpoint {
100 fn scopes(&mut self) -> &[Scope];
102
103 fn issuer(&mut self) -> &dyn Issuer;
105}
106
107pub struct Resource {
109 state: ResourceState,
110}
111
112enum ResourceState {
113 New,
115 Internalized { token: String },
117 Recovering { token: String, scopes: Vec<Scope> },
119 Err(Error),
121}
122
123#[derive(Clone)]
125pub enum Input<'req> {
126 Recovered(Option<Grant>),
128 Scopes(&'req [Scope]),
130 Request {
132 request: &'req dyn Request,
134 },
135 None,
137}
138
139#[derive(Clone, Debug)]
148pub enum Output<'machine> {
149 GetRequest,
151 Recover {
155 token: &'machine str,
157 },
158 DetermineScopes,
162 Ok(Box<Grant>),
170 Err(Error),
174}
175
176impl Resource {
177 pub fn new() -> Self {
179 Resource {
180 state: ResourceState::New,
181 }
182 }
183
184 pub fn advance(&mut self, input: Input) -> Output<'_> {
186 self.state = match (self.take(), input) {
187 (any, Input::None) => any,
188 (ResourceState::New, Input::Request { request }) => {
189 validate(request).unwrap_or_else(ResourceState::Err)
190 }
191 (ResourceState::Internalized { token }, Input::Scopes(scopes)) => get_scopes(token, scopes),
192 (ResourceState::Recovering { token: _, scopes }, Input::Recovered(grant)) => {
193 match recovered(grant, scopes) {
194 Ok(grant) => return Output::Ok(Box::new(grant)),
195 Err(err) => ResourceState::Err(err),
196 }
197 }
198 _ => return Output::Err(Error::PrimitiveError),
199 };
200
201 self.output()
202 }
203
204 fn output(&self) -> Output<'_> {
205 match &self.state {
206 ResourceState::New => Output::GetRequest,
207 ResourceState::Internalized { .. } => Output::DetermineScopes,
208 ResourceState::Recovering { token, .. } => Output::Recover { token },
209 ResourceState::Err(error) => Output::Err(error.clone()),
210 }
211 }
212
213 fn take(&mut self) -> ResourceState {
214 mem::replace(&mut self.state, ResourceState::Err(Error::PrimitiveError))
215 }
216}
217
218pub fn protect(handler: &mut dyn Endpoint, req: &dyn Request) -> Result<Grant> {
220 enum Requested {
221 None,
222 Request,
223 Scopes,
224 Grant(String),
225 }
226
227 let mut resource = Resource::new();
228 let mut requested = Requested::None;
229 loop {
230 let input = match requested {
231 Requested::None => Input::None,
232 Requested::Request => Input::Request { request: req },
233 Requested::Scopes => Input::Scopes(handler.scopes()),
234 Requested::Grant(token) => {
235 let grant = handler
236 .issuer()
237 .recover_token(&token)
238 .map_err(|_| Error::PrimitiveError)?;
239 Input::Recovered(grant)
240 }
241 };
242
243 requested = match resource.advance(input) {
244 Output::Err(error) => return Err(error),
245 Output::Ok(grant) => return Ok(*grant),
246 Output::GetRequest => Requested::Request,
247 Output::DetermineScopes => Requested::Scopes,
248 Output::Recover { token } => Requested::Grant(token.to_string()),
249 };
250 }
251}
252
253fn validate(request: &'_ dyn Request) -> Result<ResourceState> {
254 if !request.valid() {
255 return Err(Error::InvalidRequest {
256 authenticate: Authenticate::empty(),
257 });
258 }
259
260 let client_token = match request.token() {
261 Some(token) => token,
262 None => {
263 return Err(Error::NoAuthentication {
264 authenticate: Authenticate::empty(),
265 })
266 }
267 };
268
269 if !client_token
270 .to_uppercase()
271 .starts_with(&BEARER_START.to_uppercase())
272 {
273 return Err(Error::InvalidRequest {
274 authenticate: Authenticate::empty(),
275 });
276 }
277
278 let token = match client_token {
279 Cow::Borrowed(token) => token[BEARER_START.len()..].to_string(),
280 Cow::Owned(mut token) => token.split_off(BEARER_START.len()),
281 };
282
283 Ok(ResourceState::Internalized { token })
284}
285
286fn get_scopes(token: String, scopes: &'_ [Scope]) -> ResourceState {
287 ResourceState::Recovering {
288 token,
289 scopes: scopes.to_owned(),
290 }
291}
292
293fn recovered(grant: Option<Grant>, mut scopes: Vec<Scope>) -> Result<Grant> {
294 let grant = match grant {
295 Some(grant) => grant,
296 None => {
297 return Err(Error::AccessDenied {
298 failure: AccessFailure {
299 code: Some(ErrorCode::InvalidRequest),
300 },
301 authenticate: Authenticate {
302 realm: None,
303 scope: scopes.drain(..).next(),
305 },
306 });
307 }
308 };
309
310 if grant.until < Utc::now() {
311 return Err(Error::AccessDenied {
312 failure: AccessFailure {
313 code: Some(ErrorCode::InvalidToken),
314 },
315 authenticate: Authenticate::empty(),
316 });
317 }
318
319 let allowing = scopes
320 .iter()
321 .find(|resource_scope| resource_scope.allow_access(&grant.scope));
322
323 if allowing.is_none() {
324 return Err(Error::AccessDenied {
325 failure: AccessFailure {
326 code: Some(ErrorCode::InsufficientScope),
327 },
328 authenticate: Authenticate {
329 realm: None,
330 scope: scopes.drain(..).next(),
331 },
332 });
333 }
334
335 Ok(grant)
337}
338
339impl ErrorCode {
340 fn description(self) -> &'static str {
341 match self {
342 ErrorCode::InvalidRequest => "invalid_request",
343 ErrorCode::InsufficientScope => "insufficient_scope",
344 ErrorCode::InvalidToken => "invalid_token",
345 }
346 }
347}
348
349struct BearerHeader {
350 content: String,
351 first_option: bool,
352}
353
354impl BearerHeader {
355 fn new() -> Self {
356 BearerHeader {
357 content: "Bearer".to_string(),
358 first_option: true,
359 }
360 }
361
362 fn add_option(&mut self, args: fmt::Arguments) {
363 if self.first_option {
364 self.content.push(' ');
365 } else {
366 self.content.push(',');
367 }
368 fmt::write(&mut self.content, args).unwrap();
369 }
370
371 fn add_kvp(&mut self, key: &'static str, value: Option<impl fmt::Display>) {
372 if let Some(value) = value {
373 self.add_option(format_args!("{}=\"{}\"", key, value));
374 }
375 }
376
377 fn finalize(self) -> String {
378 self.content
379 }
380}
381
382impl Authenticate {
383 fn empty() -> Self {
384 Authenticate {
385 realm: None,
386 scope: None,
387 }
388 }
389
390 fn extend_header(self, header: &mut BearerHeader) {
391 header.add_kvp("realm", self.realm);
392 header.add_kvp("scope", self.scope);
393 }
394}
395
396impl AccessFailure {
397 fn extend_header(self, header: &mut BearerHeader) {
398 header.add_kvp("error", self.code.map(ErrorCode::description));
399 }
400}
401
402impl Error {
403 pub fn www_authenticate(self) -> String {
405 let mut header = BearerHeader::new();
406 match self {
407 Error::AccessDenied {
408 failure,
409 authenticate,
410 } => {
411 failure.extend_header(&mut header);
412 authenticate.extend_header(&mut header);
413 }
414 Error::NoAuthentication { authenticate } => {
415 authenticate.extend_header(&mut header);
416 }
417 Error::InvalidRequest { authenticate } => {
418 authenticate.extend_header(&mut header);
419 }
420 Error::PrimitiveError => (),
421 }
422 header.finalize()
423 }
424}