authnz_common/types/users/
authz.rs

1//! Authorization module.
2
3#[cfg(any(feature = "app-server-types", feature = "authnz-server-types"))]
4use impulse_server_kit::salvo;
5#[cfg(any(feature = "app-server-types", feature = "authnz-server-types"))]
6use salvo::oapi::ToSchema;
7use serde::{Deserialize, Serialize};
8
9use crate::{AccessTag, CBAChallengeSign, Id, TokenBundle};
10use crate::{MResult, ServerError};
11
12#[cfg_attr(any(feature = "app-server-types", feature = "authnz-server-types"), derive(ToSchema))]
13#[derive(Deserialize, Serialize, PartialEq, Eq, Clone, Debug)]
14/// Request to authorize.
15///
16/// Application clients can use this request both for check logged in
17/// and for check if user has sufficient rights to a resource (by requested tags).
18pub struct UserAuthorizeRequest {
19  /// Access token (MPAAT).
20  pub access_token: String,
21  /// Refresh token (random bytes).
22  pub refresh_token: String,
23  /// Client token (MPAAT).
24  pub client_token: Option<String>,
25  /// May contains client-based authorization challenge sign.
26  ///
27  /// If CBA is required, and CBA token is invalid, and the challenge state is provided,
28  /// and the challenge sign is valid and made by known client key, C3A will deploy new client token.
29  pub cba_challenge_sign: Option<CBAChallengeSign>,
30  /// Requested tags to authorize resource usage.
31  pub requested_tags: Vec<AccessTag>,
32}
33
34impl UserAuthorizeRequest {
35  /// Creates a new authorization request builder.
36  #[allow(clippy::new_ret_no_self)]
37  pub fn new() -> UserAuthorizeRequestBuilder {
38    Default::default()
39  }
40
41  /// Splits request into parts.
42  pub fn into_parts(self) -> (TokenBundle, Vec<AccessTag>, Option<CBAChallengeSign>) {
43    (
44      TokenBundle {
45        access: self.access_token,
46        refresh: self.refresh_token,
47        client: self.client_token,
48      },
49      self.requested_tags,
50      self.cba_challenge_sign,
51    )
52  }
53}
54
55#[derive(Default)]
56/// Request builder.
57pub struct UserAuthorizeRequestBuilder {
58  access_token: Option<String>,
59  refresh_token: Option<String>,
60  client_token: Option<String>,
61  cba_challenge_sign: Option<CBAChallengeSign>,
62  requested_tags: Option<Vec<AccessTag>>,
63}
64
65impl UserAuthorizeRequestBuilder {
66  /// Specifies simple token pair (access & refresh tokens).
67  pub fn token_pair(mut self, act: impl ToString, rft: impl ToString) -> Self {
68    self.access_token = Some(act.to_string());
69    self.refresh_token = Some(rft.to_string());
70    self
71  }
72
73  /// Specifies token triple (access, refresh & client tokens).
74  pub fn token_triple(mut self, act: impl ToString, rft: impl ToString, cba: impl ToString) -> Self {
75    self.access_token = Some(act.to_string());
76    self.refresh_token = Some(rft.to_string());
77    self.client_token = Some(cba.to_string());
78    self
79  }
80
81  /// Specifies requested tags.
82  pub fn with_tags(mut self, tags: &[impl Into<AccessTag> + Clone]) -> Self {
83    let tags = tags.iter().map(|t| t.clone().into()).collect::<Vec<_>>();
84    self.requested_tags = Some(tags);
85    self
86  }
87
88  /// Specifies no tags (just to authenticate).
89  pub fn no_tags(mut self) -> Self {
90    self.requested_tags = Some(vec![]);
91    self
92  }
93
94  /// Specifies signed challenge.
95  pub fn with_signed_challenge(mut self, sign: Vec<u8>) -> Self {
96    self.cba_challenge_sign = Some(CBAChallengeSign::new(sign));
97    self
98  }
99
100  /// Builds the request.
101  #[allow(clippy::unwrap_used)]
102  pub fn build(self) -> MResult<UserAuthorizeRequest> {
103    if self.access_token.is_none() || self.refresh_token.is_none() {
104      ServerError::from_private_str("You must specify at least access & refresh tokens to perform authorization request!")
105        .with_500()
106        .bail()?;
107    }
108
109    if self.requested_tags.is_none() {
110      ServerError::from_private_str("You must specify requested tags (`.with_tags(...)`) or no tags at all (`.no_tags()`) explicitly!")
111        .with_500()
112        .bail()?;
113    }
114
115    let req = UserAuthorizeRequest {
116      access_token: unsafe { self.access_token.unwrap_unchecked() },
117      refresh_token: unsafe { self.refresh_token.unwrap_unchecked() },
118      client_token: self.client_token,
119      requested_tags: unsafe { self.requested_tags.unwrap_unchecked() },
120      cba_challenge_sign: self.cba_challenge_sign,
121    };
122
123    Ok(req)
124  }
125}
126
127#[cfg_attr(any(feature = "app-server-types", feature = "authnz-server-types"), derive(ToSchema))]
128#[derive(Deserialize, Serialize, PartialEq, Eq, Hash, Clone, Debug)]
129/// Authorize response.
130pub struct UserAuthorizeResponse {
131  /// Grants permissions to the resource.
132  pub approved: bool,
133  /// Identifies the user.
134  pub id: Option<Id>,
135  /// May contains new access token.
136  ///
137  /// Application backend must update it in the application client.
138  pub new_access_token: Option<String>,
139  /// May contains new client-based authorization challenge.
140  ///
141  /// Application backend must transfer this challenge to the application client
142  /// and repeat request after getting `cba_challenge_sign` from it.
143  /// Note that application backend also must transfer challenge state from the headers!
144  pub new_cba_challenge: Option<Vec<u8>>,
145  /// May contains new client token.
146  ///
147  /// Application backend must update it in the application client.
148  pub new_cba_token: Option<String>,
149}
150
151#[derive(Deserialize, Serialize)]
152/// Simple response with permission grant.
153pub struct ApplicationAuthorizeResponse {
154  /// Authorization status.
155  pub authorized: bool,
156  /// Authorized user's ID.
157  pub user_id: Option<Id>,
158}