1use std::borrow::Cow;
3use std::result::Result as StdResult;
4
5use url::Url;
6use chrono::{Duration, Utc};
7
8use crate::code_grant::error::{AuthorizationError, AuthorizationErrorType};
9use crate::primitives::authorizer::Authorizer;
10use crate::primitives::registrar::{ClientUrl, ExactUrl, Registrar, RegistrarError, PreGrant};
11use crate::primitives::grant::{Extensions, Grant};
12use crate::{endpoint::Scope, endpoint::Solicitation, primitives::registrar::BoundClient};
13
14pub trait Request {
16 fn valid(&self) -> bool;
21
22 fn client_id(&self) -> Option<Cow<str>>;
24
25 fn scope(&self) -> Option<Cow<str>>;
27
28 fn redirect_uri(&self) -> Option<Cow<str>>;
30
31 fn state(&self) -> Option<Cow<str>>;
33
34 fn response_type(&self) -> Option<Cow<str>>;
36
37 fn extension(&self, key: &str) -> Option<Cow<str>>;
39}
40
41pub trait Extension {
45 fn extend(&mut self, request: &dyn Request) -> std::result::Result<Extensions, ()>;
47}
48
49impl Extension for () {
50 fn extend(&mut self, _: &dyn Request) -> std::result::Result<Extensions, ()> {
51 Ok(Extensions::new())
52 }
53}
54
55pub trait Endpoint {
61 fn registrar(&self) -> &dyn Registrar;
63
64 fn authorizer(&mut self) -> &mut dyn Authorizer;
66
67 fn extension(&mut self) -> &mut dyn Extension;
71}
72
73pub struct Authorization {
75 state: AuthorizationState,
76 extensions: Option<Extensions>,
77 scope: Option<Scope>,
78}
79
80enum AuthorizationState {
81 Binding {
83 client_id: String,
84 redirect_uri: Option<ExactUrl>,
85 },
86 Extending {
87 bound_client: BoundClient<'static>,
88 },
89 Negotiating {
90 bound_client: BoundClient<'static>,
91 },
92 Pending {
93 pre_grant: PreGrant,
94 state: Option<String>,
95 extensions: Extensions,
96 },
97 Err(Error),
98}
99
100pub enum Input<'machine> {
102 Bound {
104 request: &'machine dyn Request,
106 bound_client: BoundClient<'static>,
108 },
109 Extended(Extensions),
111 Negotiated {
113 pre_grant: PreGrant,
115 state: Option<String>,
117 },
118 Finished,
120 None,
122}
123
124pub enum Output<'machine> {
130 Bind {
132 client_id: String,
134 redirect_uri: Option<ExactUrl>,
136 },
137 Extend,
139 Negotiate {
141 bound_client: &'machine BoundClient<'static>,
143 scope: Option<Scope>,
145 },
146 Ok {
149 pre_grant: PreGrant,
151 state: Option<String>,
153 extensions: Extensions,
155 },
156 Err(Error),
160}
161
162impl Authorization {
163 pub fn new(request: &dyn Request) -> Self {
165 Authorization {
166 state: Self::validate(request).unwrap_or_else(AuthorizationState::Err),
167 extensions: None,
168 scope: None,
169 }
170 }
171
172 pub fn advance<'req>(&mut self, input: Input<'req>) -> Output<'_> {
174 self.state = match (self.take(), input) {
175 (current, Input::None) => current,
176 (
177 AuthorizationState::Binding { .. },
178 Input::Bound {
179 request,
180 bound_client,
181 },
182 ) => self
183 .bound(request, bound_client)
184 .unwrap_or_else(AuthorizationState::Err),
185 (AuthorizationState::Extending { bound_client }, Input::Extended(grant_extension)) => {
186 self.extended(grant_extension, bound_client)
187 }
188 (AuthorizationState::Negotiating { .. }, Input::Negotiated { pre_grant, state }) => {
189 self.negotiated(state, pre_grant)
190 }
191 (AuthorizationState::Err(err), _) => AuthorizationState::Err(err),
192 (_, _) => AuthorizationState::Err(Error::PrimitiveError),
193 };
194
195 self.output()
196 }
197
198 fn output(&self) -> Output<'_> {
199 match &self.state {
200 AuthorizationState::Err(err) => Output::Err(err.clone()),
201 AuthorizationState::Binding {
202 client_id,
203 redirect_uri,
204 } => Output::Bind {
205 client_id: client_id.to_string(),
206 redirect_uri: (*redirect_uri).clone(),
207 },
208 AuthorizationState::Extending { .. } => Output::Extend,
209 AuthorizationState::Negotiating { bound_client } => Output::Negotiate {
210 bound_client: &bound_client,
211 scope: self.scope.clone(),
212 },
213 AuthorizationState::Pending {
214 pre_grant,
215 state,
216 extensions,
217 } => Output::Ok {
218 pre_grant: pre_grant.clone(),
219 state: state.clone(),
220 extensions: extensions.clone(),
221 },
222 }
223 }
224
225 fn bound(
226 &mut self, request: &dyn Request, bound_client: BoundClient<'static>,
227 ) -> Result<AuthorizationState> {
228 match request.response_type() {
231 Some(ref method) if method.as_ref() == "code" => (),
232 _ => {
233 let prepared_error = ErrorUrl::with_request(
234 request,
235 (*bound_client.redirect_uri).to_url(),
236 AuthorizationErrorType::UnsupportedResponseType,
237 );
238 return Err(Error::Redirect(prepared_error));
239 }
240 }
241
242 let scope = request.scope();
246 self.scope = match scope.map(|scope| scope.as_ref().parse()) {
247 None => None,
248 Some(Err(_)) => {
249 let prepared_error = ErrorUrl::with_request(
250 request,
251 (*bound_client.redirect_uri).to_url(),
252 AuthorizationErrorType::InvalidScope,
253 );
254 return Err(Error::Redirect(prepared_error));
255 }
256 Some(Ok(scope)) => Some(scope),
257 };
258
259 Ok(AuthorizationState::Extending { bound_client })
260 }
261
262 fn extended(
263 &mut self, grant_extension: Extensions, bound_client: BoundClient<'static>,
264 ) -> AuthorizationState {
265 self.extensions = Some(grant_extension);
266 AuthorizationState::Negotiating { bound_client }
267 }
268
269 fn negotiated(&mut self, state: Option<String>, pre_grant: PreGrant) -> AuthorizationState {
270 AuthorizationState::Pending {
271 pre_grant,
272 state,
273 extensions: self.extensions.clone().expect("Should have extensions by now"),
274 }
275 }
276
277 fn take(&mut self) -> AuthorizationState {
278 std::mem::replace(&mut self.state, AuthorizationState::Err(Error::PrimitiveError))
279 }
280
281 fn validate(request: &dyn Request) -> Result<AuthorizationState> {
282 if !request.valid() {
283 return Err(Error::Ignore);
284 };
285
286 let client_id = request.client_id().ok_or(Error::Ignore)?;
288 let redirect_uri: Option<Cow<ExactUrl>> = match request.redirect_uri() {
289 None => None,
290 Some(ref uri) => {
291 let parsed = uri.parse().map_err(|_| Error::Ignore)?;
292 Some(Cow::Owned(parsed))
293 }
294 };
295
296 Ok(AuthorizationState::Binding {
297 client_id: client_id.into_owned(),
298 redirect_uri: redirect_uri.map(|uri| uri.into_owned()),
299 })
300 }
301}
302
303pub fn authorization_code(handler: &mut dyn Endpoint, request: &dyn Request) -> self::Result<Pending> {
313 enum Requested {
314 None,
315 Bind {
316 client_id: String,
317 redirect_uri: Option<ExactUrl>,
318 },
319 Extend,
320 Negotiate {
321 client_id: String,
322 redirect_uri: Url,
323 scope: Option<Scope>,
324 },
325 }
326
327 let mut authorization = Authorization::new(request);
328 let mut requested = Requested::None;
329 let mut the_redirect_uri = None;
330
331 loop {
332 let input = match requested {
333 Requested::None => Input::None,
334 Requested::Bind {
335 client_id,
336 redirect_uri,
337 } => {
338 let client_url = ClientUrl {
339 client_id: Cow::Owned(client_id),
340 redirect_uri: redirect_uri.map(Cow::Owned),
341 };
342 let bound_client = match handler.registrar().bound_redirect(client_url) {
343 Err(RegistrarError::Unspecified) => return Err(Error::Ignore),
344 Err(RegistrarError::PrimitiveError) => return Err(Error::PrimitiveError),
345 Ok(pre_grant) => pre_grant,
346 };
347 the_redirect_uri = Some(bound_client.redirect_uri.clone().into_owned());
348 Input::Bound {
349 request,
350 bound_client,
351 }
352 }
353 Requested::Extend => {
354 let grant_extension = match handler.extension().extend(request) {
355 Ok(extension_data) => extension_data,
356 Err(()) => {
357 let prepared_error = ErrorUrl::with_request(
358 request,
359 the_redirect_uri.unwrap().into(),
360 AuthorizationErrorType::InvalidRequest,
361 );
362 return Err(Error::Redirect(prepared_error));
363 }
364 };
365 Input::Extended(grant_extension)
366 }
367 Requested::Negotiate {
368 client_id,
369 redirect_uri,
370 scope,
371 } => {
372 let bound_client = BoundClient {
373 client_id: Cow::Owned(client_id),
374 redirect_uri: Cow::Owned(redirect_uri.clone().into()),
375 };
376 let pre_grant = handler
377 .registrar()
378 .negotiate(bound_client, scope)
379 .map_err(|err| match err {
380 RegistrarError::PrimitiveError => Error::PrimitiveError,
381 RegistrarError::Unspecified => {
382 let prepared_error = ErrorUrl::with_request(
383 request,
384 redirect_uri,
385 AuthorizationErrorType::InvalidScope,
386 );
387 Error::Redirect(prepared_error)
388 }
389 })?;
390 Input::Negotiated {
391 pre_grant,
392 state: request.state().map(|s| s.into_owned()),
393 }
394 }
395 };
396
397 requested = match authorization.advance(input) {
398 Output::Bind {
399 client_id,
400 redirect_uri,
401 } => Requested::Bind {
402 client_id,
403 redirect_uri,
404 },
405 Output::Extend => Requested::Extend,
406 Output::Negotiate { bound_client, scope } => Requested::Negotiate {
407 client_id: bound_client.client_id.clone().into_owned(),
408 redirect_uri: bound_client.redirect_uri.to_url(),
409 scope,
410 },
411 Output::Ok {
412 pre_grant,
413 state,
414 extensions,
415 } => {
416 return Ok(Pending {
417 pre_grant,
418 state,
419 extensions,
420 })
421 }
422 Output::Err(e) => return Err(e),
423 };
424 }
425}
426
427pub struct Pending {
433 pre_grant: PreGrant,
434 state: Option<String>,
435 extensions: Extensions,
436}
437
438impl Pending {
439 pub fn as_solicitation(&self) -> Solicitation<'_> {
441 Solicitation {
442 grant: Cow::Borrowed(&self.pre_grant),
443 state: self.state.as_ref().map(|s| Cow::Borrowed(&**s)),
444 }
445 }
446
447 pub fn deny(self) -> Result<Url> {
449 let url = self.pre_grant.redirect_uri;
450 let mut error = AuthorizationError::default();
451 error.set_type(AuthorizationErrorType::AccessDenied);
452 let error = ErrorUrl::new_generic(url.into_url(), self.state, error);
453 Err(Error::Redirect(error))
454 }
455
456 pub fn authorize(self, handler: &mut dyn Endpoint, owner_id: Cow<str>) -> Result<Url> {
461 let mut url = self.pre_grant.redirect_uri.to_url();
462
463 let grant = handler
464 .authorizer()
465 .authorize(Grant {
466 owner_id: owner_id.into_owned(),
467 client_id: self.pre_grant.client_id,
468 redirect_uri: self.pre_grant.redirect_uri.into_url(),
469 scope: self.pre_grant.scope,
470 until: Utc::now() + Duration::minutes(10),
471 extensions: self.extensions,
472 })
473 .map_err(|()| Error::PrimitiveError)?;
474
475 url.query_pairs_mut()
476 .append_pair("code", grant.as_str())
477 .extend_pairs(self.state.map(|v| ("state", v)))
478 .finish();
479 Ok(url)
480 }
481
482 pub fn pre_grant(&self) -> &PreGrant {
485 &self.pre_grant
486 }
487}
488
489#[derive(Clone)]
494pub enum Error {
495 Ignore,
497
498 Redirect(ErrorUrl),
500
501 PrimitiveError,
505}
506
507#[derive(Clone)]
512pub struct ErrorUrl {
513 base_uri: Url,
514 error: AuthorizationError,
515}
516
517type Result<T> = StdResult<T, Error>;
518
519impl ErrorUrl {
520 fn new_generic<S>(mut url: Url, state: Option<S>, error: AuthorizationError) -> ErrorUrl
522 where
523 S: AsRef<str>,
524 {
525 url.query_pairs_mut()
526 .extend_pairs(state.as_ref().map(|st| ("state", st.as_ref())));
527 ErrorUrl { base_uri: url, error }
528 }
529
530 pub fn new(url: Url, state: Option<&str>, error: AuthorizationError) -> ErrorUrl {
532 ErrorUrl::new_generic(url, state, error)
533 }
534
535 pub fn with_request(
537 request: &dyn Request, redirect_uri: Url, err_type: AuthorizationErrorType,
538 ) -> ErrorUrl {
539 let mut err = ErrorUrl::new(
540 redirect_uri,
541 request.state().as_deref(),
542 AuthorizationError::default(),
543 );
544 err.description().set_type(err_type);
545 err
546 }
547
548 pub fn description(&mut self) -> &mut AuthorizationError {
550 &mut self.error
551 }
552}
553
554impl Error {
555 pub fn description(&mut self) -> Option<&mut AuthorizationError> {
560 match self {
561 Error::Ignore => None,
562 Error::Redirect(inner) => Some(inner.description()),
563 Error::PrimitiveError => None,
564 }
565 }
566}
567
568impl Into<Url> for ErrorUrl {
569 fn into(self) -> Url {
571 let mut url = self.base_uri;
572 url.query_pairs_mut().extend_pairs(self.error.into_iter());
573 url
574 }
575}