1use std::time::{SystemTime, UNIX_EPOCH};
5
6use hyper::{header, Body, Request};
7use jsonwebtoken;
8use serde_derive::{Deserialize, Serialize};
9use thiserror::Error;
10use x509_parser::pem::Pem;
11
12use crate::certificate_parser::{get_email_from_dn, get_email_from_san, get_uid};
13
14mod certificate_parser;
15
16#[derive(Error, Debug)]
17pub enum CustomError {
18 #[error("System time is before unix time")]
19 SystemTimeError(#[from] std::time::SystemTimeError),
20 #[error("JWT Error: {0}")]
21 JWTError(#[from] jsonwebtoken::errors::Error),
22 #[error("Error converting header value to string: {0}")]
23 ToStrError(#[from] header::ToStrError),
24 #[error("Missing x-ssl-client-escaped-cert header")]
25 MissingHeader,
26 #[error("No certificate given")]
27 NoCertificate,
28 #[error("SAN exists but could not be parsed")]
29 InvalidSAN,
30 #[error("Could not get email from certificate")]
31 NoEmail,
32 #[error("Certificate has no Common Name")]
33 NoCommonName,
34 #[error("Invalid urlencoding {0}")]
35 UrlEncoding(#[from] urlencoding::FromUrlEncodingError),
36 #[error("Decoding cert: {0}")]
37 PEM(#[from] x509_parser::prelude::PEMError),
38 #[error("Decoding cert: {0}")]
39 X509(#[from] x509_parser::prelude::X509Error),
40 #[error("Decoding cert: {0}")]
41 Nom(#[from] x509_parser::nom::Err<x509_parser::prelude::X509Error>),
42 #[error("HttpError: {0}")]
43 HttpError(#[from] hyper::http::Error),
44 #[error("")]
45 Infallible(#[from] std::convert::Infallible),
46 #[error("Reqwest Error")]
47 Reqwest(#[from] reqwest::Error),
48 #[error("Hyper Error")]
49 HyperError(#[from] hyper::Error),
50 #[error("Supplied permissions are empty")]
51 PermissionEmptyError,
52 #[error("Supplied entity does not match the List of allowed entities")]
53 PermissionNotMatchedError,
54}
55
56pub type Result<T> = std::result::Result<T, CustomError>;
58
59#[derive(Clone, Debug, Serialize)]
61pub struct Claims {
62 pub email: String,
64
65 pub name: String,
67
68 pub uid: String,
70
71 pub exp: u64,
77
78 pub iat: u64,
84}
85
86#[derive(Clone, Debug, Deserialize)]
91pub struct Permissions {
92 #[serde(default)]
94 pub allowed_uids: Vec<String>,
95}
96
97pub fn get_claims(req: Request<Body>) -> crate::Result<Claims> {
99 let escaped_cert_str = req
100 .headers()
101 .get("x-ssl-client-escaped-cert")
102 .ok_or(CustomError::MissingHeader)?
103 .to_str()?;
104
105 let cert_str = urlencoding::decode(escaped_cert_str)?;
106
107 let cert_pem = Pem::iter_from_buffer(&cert_str.as_bytes())
108 .next()
109 .ok_or(CustomError::NoCertificate)??;
110
111 let cert = cert_pem.parse_x509()?;
112
113 let email = get_email_from_san(&cert)
114 .transpose()
115 .or_else(|| get_email_from_dn(&cert).transpose())
116 .ok_or(CustomError::NoEmail)??;
117
118 let name = cert
119 .subject()
120 .iter_common_name()
121 .next()
122 .map(|x| Ok::<_, CustomError>(x.as_str()?))
123 .transpose()?
124 .ok_or(CustomError::NoCommonName)?;
125
126 let uid = get_uid(&cert)?.unwrap_or(name);
127
128 let iat = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
129 let exp = iat + 3600;
130
131 Ok(Claims {
132 email: email.to_string(),
133 name: name.to_string(),
134 uid: uid.to_string(),
135 exp,
136 iat,
137 })
138}
139
140pub fn is_allowed_by_uid(user: &Claims, permissions: &Permissions) -> crate::Result<()> {
142 if permissions.allowed_uids.is_empty() {
143 return Err(CustomError::PermissionEmptyError);
144 }
145
146 if permissions.allowed_uids.iter().any(|u| u.eq(&user.uid)) {
147 Ok(())
148 } else {
149 Err(CustomError::PermissionNotMatchedError)
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use crate::{is_allowed_by_uid, Claims, CustomError, Permissions};
156
157 #[test]
158 fn test_is_allowed_by_uid() {
159 let user_allowed = Claims {
160 email: "nya@nyantec.com".to_string(),
161 name: "Some Name".to_string(),
162 uid: "nya".to_string(),
163 exp: 0,
164 iat: 0,
165 };
166 let user_denied = Claims {
167 email: "nyet@nyantec.com".to_string(),
168 name: "Some Name".to_string(),
169 uid: "nyet".to_string(),
170 exp: 0,
171 iat: 0,
172 };
173
174 let permissions = Permissions {
175 allowed_uids: vec![user_allowed.uid.clone()],
176 };
177 let permissions_empty = Permissions {
178 allowed_uids: vec![],
179 };
180
181 assert_eq!(
182 is_allowed_by_uid(&user_allowed, &permissions).is_ok(),
183 Ok::<_, CustomError>(()).is_ok()
184 );
185 assert_eq!(
186 is_allowed_by_uid(&user_denied, &permissions).is_ok(),
187 Err::<(), CustomError>(CustomError::PermissionNotMatchedError).is_ok()
188 );
189 assert_eq!(
190 is_allowed_by_uid(&user_allowed, &permissions_empty).is_ok(),
191 Err::<(), CustomError>(CustomError::PermissionEmptyError).is_ok()
192 )
193 }
194}