1 //
2 // JWT Handling
3 //
4 use chrono::{Duration, Utc};
5 use num_traits::FromPrimitive;
6 use once_cell::sync::Lazy;
7
8 use jsonwebtoken::{self, Algorithm, DecodingKey, EncodingKey, Header};
9 use serde::de::DeserializeOwned;
10 use serde::ser::Serialize;
11
12 use crate::{
13 error::{Error, MapResult},
14 util::read_file,
15 CONFIG,
16 };
17
18 const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
19
20 pub static DEFAULT_VALIDITY: Lazy<Duration> = Lazy::new(|| Duration::hours(2));
21 static JWT_HEADER: Lazy<Header> = Lazy::new(|| Header::new(JWT_ALGORITHM));
22
23 pub static JWT_LOGIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|login", CONFIG.domain_origin()));
24 static JWT_INVITE_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|invite", CONFIG.domain_origin()));
25 static JWT_EMERGENCY_ACCESS_INVITE_ISSUER: Lazy<String> =
26 Lazy::new(|| format!("{}|emergencyaccessinvite", CONFIG.domain_origin()));
27 static JWT_DELETE_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|delete", CONFIG.domain_origin()));
28 static JWT_VERIFYEMAIL_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|verifyemail", CONFIG.domain_origin()));
29 static JWT_ADMIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|admin", CONFIG.domain_origin()));
30 static JWT_SEND_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|send", CONFIG.domain_origin()));
31
32 static PRIVATE_RSA_KEY_VEC: Lazy<Vec<u8>> = Lazy::new(|| {
33 read_file(&CONFIG.private_rsa_key()).unwrap_or_else(|e| panic!("Error loading private RSA Key.\n{}", e))
34 });
35 static PRIVATE_RSA_KEY: Lazy<EncodingKey> = Lazy::new(|| {
36 EncodingKey::from_rsa_pem(&PRIVATE_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding private RSA Key.\n{}", e))
37 });
38 static PUBLIC_RSA_KEY_VEC: Lazy<Vec<u8>> = Lazy::new(|| {
39 read_file(&CONFIG.public_rsa_key()).unwrap_or_else(|e| panic!("Error loading public RSA Key.\n{}", e))
40 });
41 static PUBLIC_RSA_KEY: Lazy<DecodingKey> = Lazy::new(|| {
42 DecodingKey::from_rsa_pem(&PUBLIC_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding public RSA Key.\n{}", e))
43 });
44
load_keys()45 pub fn load_keys() {
46 Lazy::force(&PRIVATE_RSA_KEY);
47 Lazy::force(&PUBLIC_RSA_KEY);
48 }
49
encode_jwt<T: Serialize>(claims: &T) -> String50 pub fn encode_jwt<T: Serialize>(claims: &T) -> String {
51 match jsonwebtoken::encode(&JWT_HEADER, claims, &PRIVATE_RSA_KEY) {
52 Ok(token) => token,
53 Err(e) => panic!("Error encoding jwt {}", e),
54 }
55 }
56
decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Error>57 fn decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Error> {
58 let validation = jsonwebtoken::Validation {
59 leeway: 30, // 30 seconds
60 validate_exp: true,
61 validate_nbf: true,
62 aud: None,
63 iss: Some(issuer),
64 sub: None,
65 algorithms: vec![JWT_ALGORITHM],
66 };
67
68 let token = token.replace(char::is_whitespace, "");
69 jsonwebtoken::decode(&token, &PUBLIC_RSA_KEY, &validation).map(|d| d.claims).map_res("Error decoding JWT")
70 }
71
decode_login(token: &str) -> Result<LoginJwtClaims, Error>72 pub fn decode_login(token: &str) -> Result<LoginJwtClaims, Error> {
73 decode_jwt(token, JWT_LOGIN_ISSUER.to_string())
74 }
75
decode_invite(token: &str) -> Result<InviteJwtClaims, Error>76 pub fn decode_invite(token: &str) -> Result<InviteJwtClaims, Error> {
77 decode_jwt(token, JWT_INVITE_ISSUER.to_string())
78 }
79
decode_emergency_access_invite(token: &str) -> Result<EmergencyAccessInviteJwtClaims, Error>80 pub fn decode_emergency_access_invite(token: &str) -> Result<EmergencyAccessInviteJwtClaims, Error> {
81 decode_jwt(token, JWT_EMERGENCY_ACCESS_INVITE_ISSUER.to_string())
82 }
83
decode_delete(token: &str) -> Result<BasicJwtClaims, Error>84 pub fn decode_delete(token: &str) -> Result<BasicJwtClaims, Error> {
85 decode_jwt(token, JWT_DELETE_ISSUER.to_string())
86 }
87
decode_verify_email(token: &str) -> Result<BasicJwtClaims, Error>88 pub fn decode_verify_email(token: &str) -> Result<BasicJwtClaims, Error> {
89 decode_jwt(token, JWT_VERIFYEMAIL_ISSUER.to_string())
90 }
91
decode_admin(token: &str) -> Result<BasicJwtClaims, Error>92 pub fn decode_admin(token: &str) -> Result<BasicJwtClaims, Error> {
93 decode_jwt(token, JWT_ADMIN_ISSUER.to_string())
94 }
95
decode_send(token: &str) -> Result<BasicJwtClaims, Error>96 pub fn decode_send(token: &str) -> Result<BasicJwtClaims, Error> {
97 decode_jwt(token, JWT_SEND_ISSUER.to_string())
98 }
99
100 #[derive(Debug, Serialize, Deserialize)]
101 pub struct LoginJwtClaims {
102 // Not before
103 pub nbf: i64,
104 // Expiration time
105 pub exp: i64,
106 // Issuer
107 pub iss: String,
108 // Subject
109 pub sub: String,
110
111 pub premium: bool,
112 pub name: String,
113 pub email: String,
114 pub email_verified: bool,
115
116 pub orgowner: Vec<String>,
117 pub orgadmin: Vec<String>,
118 pub orguser: Vec<String>,
119 pub orgmanager: Vec<String>,
120
121 // user security_stamp
122 pub sstamp: String,
123 // device uuid
124 pub device: String,
125 // [ "api", "offline_access" ]
126 pub scope: Vec<String>,
127 // [ "Application" ]
128 pub amr: Vec<String>,
129 }
130
131 #[derive(Debug, Serialize, Deserialize)]
132 pub struct InviteJwtClaims {
133 // Not before
134 pub nbf: i64,
135 // Expiration time
136 pub exp: i64,
137 // Issuer
138 pub iss: String,
139 // Subject
140 pub sub: String,
141
142 pub email: String,
143 pub org_id: Option<String>,
144 pub user_org_id: Option<String>,
145 pub invited_by_email: Option<String>,
146 }
147
generate_invite_claims( uuid: String, email: String, org_id: Option<String>, user_org_id: Option<String>, invited_by_email: Option<String>, ) -> InviteJwtClaims148 pub fn generate_invite_claims(
149 uuid: String,
150 email: String,
151 org_id: Option<String>,
152 user_org_id: Option<String>,
153 invited_by_email: Option<String>,
154 ) -> InviteJwtClaims {
155 let time_now = Utc::now().naive_utc();
156 InviteJwtClaims {
157 nbf: time_now.timestamp(),
158 exp: (time_now + Duration::days(5)).timestamp(),
159 iss: JWT_INVITE_ISSUER.to_string(),
160 sub: uuid,
161 email,
162 org_id,
163 user_org_id,
164 invited_by_email,
165 }
166 }
167
168 #[derive(Debug, Serialize, Deserialize)]
169 pub struct EmergencyAccessInviteJwtClaims {
170 // Not before
171 pub nbf: i64,
172 // Expiration time
173 pub exp: i64,
174 // Issuer
175 pub iss: String,
176 // Subject
177 pub sub: String,
178
179 pub email: String,
180 pub emer_id: Option<String>,
181 pub grantor_name: Option<String>,
182 pub grantor_email: Option<String>,
183 }
184
generate_emergency_access_invite_claims( uuid: String, email: String, emer_id: Option<String>, grantor_name: Option<String>, grantor_email: Option<String>, ) -> EmergencyAccessInviteJwtClaims185 pub fn generate_emergency_access_invite_claims(
186 uuid: String,
187 email: String,
188 emer_id: Option<String>,
189 grantor_name: Option<String>,
190 grantor_email: Option<String>,
191 ) -> EmergencyAccessInviteJwtClaims {
192 let time_now = Utc::now().naive_utc();
193 EmergencyAccessInviteJwtClaims {
194 nbf: time_now.timestamp(),
195 exp: (time_now + Duration::days(5)).timestamp(),
196 iss: JWT_EMERGENCY_ACCESS_INVITE_ISSUER.to_string(),
197 sub: uuid,
198 email,
199 emer_id,
200 grantor_name,
201 grantor_email,
202 }
203 }
204
205 #[derive(Debug, Serialize, Deserialize)]
206 pub struct BasicJwtClaims {
207 // Not before
208 pub nbf: i64,
209 // Expiration time
210 pub exp: i64,
211 // Issuer
212 pub iss: String,
213 // Subject
214 pub sub: String,
215 }
216
generate_delete_claims(uuid: String) -> BasicJwtClaims217 pub fn generate_delete_claims(uuid: String) -> BasicJwtClaims {
218 let time_now = Utc::now().naive_utc();
219 BasicJwtClaims {
220 nbf: time_now.timestamp(),
221 exp: (time_now + Duration::days(5)).timestamp(),
222 iss: JWT_DELETE_ISSUER.to_string(),
223 sub: uuid,
224 }
225 }
226
generate_verify_email_claims(uuid: String) -> BasicJwtClaims227 pub fn generate_verify_email_claims(uuid: String) -> BasicJwtClaims {
228 let time_now = Utc::now().naive_utc();
229 BasicJwtClaims {
230 nbf: time_now.timestamp(),
231 exp: (time_now + Duration::days(5)).timestamp(),
232 iss: JWT_VERIFYEMAIL_ISSUER.to_string(),
233 sub: uuid,
234 }
235 }
236
generate_admin_claims() -> BasicJwtClaims237 pub fn generate_admin_claims() -> BasicJwtClaims {
238 let time_now = Utc::now().naive_utc();
239 BasicJwtClaims {
240 nbf: time_now.timestamp(),
241 exp: (time_now + Duration::minutes(20)).timestamp(),
242 iss: JWT_ADMIN_ISSUER.to_string(),
243 sub: "admin_panel".to_string(),
244 }
245 }
246
generate_send_claims(send_id: &str, file_id: &str) -> BasicJwtClaims247 pub fn generate_send_claims(send_id: &str, file_id: &str) -> BasicJwtClaims {
248 let time_now = Utc::now().naive_utc();
249 BasicJwtClaims {
250 nbf: time_now.timestamp(),
251 exp: (time_now + Duration::minutes(2)).timestamp(),
252 iss: JWT_SEND_ISSUER.to_string(),
253 sub: format!("{}/{}", send_id, file_id),
254 }
255 }
256
257 //
258 // Bearer token authentication
259 //
260 use rocket::request::{FromRequest, Outcome, Request};
261
262 use crate::db::{
263 models::{CollectionUser, Device, User, UserOrgStatus, UserOrgType, UserOrganization, UserStampException},
264 DbConn,
265 };
266
267 pub struct Host {
268 pub host: String,
269 }
270
271 impl<'a, 'r> FromRequest<'a, 'r> for Host {
272 type Error = &'static str;
273
from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>274 fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
275 let headers = request.headers();
276
277 // Get host
278 let host = if CONFIG.domain_set() {
279 CONFIG.domain()
280 } else if let Some(referer) = headers.get_one("Referer") {
281 referer.to_string()
282 } else {
283 // Try to guess from the headers
284 use std::env;
285
286 let protocol = if let Some(proto) = headers.get_one("X-Forwarded-Proto") {
287 proto
288 } else if env::var("ROCKET_TLS").is_ok() {
289 "https"
290 } else {
291 "http"
292 };
293
294 let host = if let Some(host) = headers.get_one("X-Forwarded-Host") {
295 host
296 } else if let Some(host) = headers.get_one("Host") {
297 host
298 } else {
299 ""
300 };
301
302 format!("{}://{}", protocol, host)
303 };
304
305 Outcome::Success(Host {
306 host,
307 })
308 }
309 }
310
311 pub struct Headers {
312 pub host: String,
313 pub device: Device,
314 pub user: User,
315 }
316
317 impl<'a, 'r> FromRequest<'a, 'r> for Headers {
318 type Error = &'static str;
319
from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>320 fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
321 let headers = request.headers();
322
323 let host = match Host::from_request(request) {
324 Outcome::Forward(_) => return Outcome::Forward(()),
325 Outcome::Failure(f) => return Outcome::Failure(f),
326 Outcome::Success(host) => host.host,
327 };
328
329 // Get access_token
330 let access_token: &str = match headers.get_one("Authorization") {
331 Some(a) => match a.rsplit("Bearer ").next() {
332 Some(split) => split,
333 None => err_handler!("No access token provided"),
334 },
335 None => err_handler!("No access token provided"),
336 };
337
338 // Check JWT token is valid and get device and user from it
339 let claims = match decode_login(access_token) {
340 Ok(claims) => claims,
341 Err(_) => err_handler!("Invalid claim"),
342 };
343
344 let device_uuid = claims.device;
345 let user_uuid = claims.sub;
346
347 let conn = match request.guard::<DbConn>() {
348 Outcome::Success(conn) => conn,
349 _ => err_handler!("Error getting DB"),
350 };
351
352 let device = match Device::find_by_uuid(&device_uuid, &conn) {
353 Some(device) => device,
354 None => err_handler!("Invalid device id"),
355 };
356
357 let user = match User::find_by_uuid(&user_uuid, &conn) {
358 Some(user) => user,
359 None => err_handler!("Device has no user associated"),
360 };
361
362 if user.security_stamp != claims.sstamp {
363 if let Some(stamp_exception) =
364 user.stamp_exception.as_deref().and_then(|s| serde_json::from_str::<UserStampException>(s).ok())
365 {
366 let current_route = match request.route().and_then(|r| r.name) {
367 Some(name) => name,
368 _ => err_handler!("Error getting current route for stamp exception"),
369 };
370
371 // Check if the stamp exception has expired first.
372 // Then, check if the current route matches any of the allowed routes.
373 // After that check the stamp in exception matches the one in the claims.
374 if Utc::now().naive_utc().timestamp() > stamp_exception.expire {
375 // If the stamp exception has been expired remove it from the database.
376 // This prevents checking this stamp exception for new requests.
377 let mut user = user;
378 user.reset_stamp_exception();
379 if let Err(e) = user.save(&conn) {
380 error!("Error updating user: {:#?}", e);
381 }
382 err_handler!("Stamp exception is expired")
383 } else if !stamp_exception.routes.contains(¤t_route.to_string()) {
384 err_handler!("Invalid security stamp: Current route and exception route do not match")
385 } else if stamp_exception.security_stamp != claims.sstamp {
386 err_handler!("Invalid security stamp for matched stamp exception")
387 }
388 } else {
389 err_handler!("Invalid security stamp")
390 }
391 }
392
393 Outcome::Success(Headers {
394 host,
395 device,
396 user,
397 })
398 }
399 }
400
401 pub struct OrgHeaders {
402 pub host: String,
403 pub device: Device,
404 pub user: User,
405 pub org_user_type: UserOrgType,
406 pub org_user: UserOrganization,
407 pub org_id: String,
408 }
409
410 // org_id is usually the second path param ("/organizations/<org_id>"),
411 // but there are cases where it is a query value.
412 // First check the path, if this is not a valid uuid, try the query values.
get_org_id(request: &Request) -> Option<String>413 fn get_org_id(request: &Request) -> Option<String> {
414 if let Some(Ok(org_id)) = request.get_param::<String>(1) {
415 if uuid::Uuid::parse_str(&org_id).is_ok() {
416 return Some(org_id);
417 }
418 }
419
420 if let Some(Ok(org_id)) = request.get_query_value::<String>("organizationId") {
421 if uuid::Uuid::parse_str(&org_id).is_ok() {
422 return Some(org_id);
423 }
424 }
425
426 None
427 }
428
429 impl<'a, 'r> FromRequest<'a, 'r> for OrgHeaders {
430 type Error = &'static str;
431
from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>432 fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
433 match request.guard::<Headers>() {
434 Outcome::Forward(_) => Outcome::Forward(()),
435 Outcome::Failure(f) => Outcome::Failure(f),
436 Outcome::Success(headers) => {
437 match get_org_id(request) {
438 Some(org_id) => {
439 let conn = match request.guard::<DbConn>() {
440 Outcome::Success(conn) => conn,
441 _ => err_handler!("Error getting DB"),
442 };
443
444 let user = headers.user;
445 let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn) {
446 Some(user) => {
447 if user.status == UserOrgStatus::Confirmed as i32 {
448 user
449 } else {
450 err_handler!("The current user isn't confirmed member of the organization")
451 }
452 }
453 None => err_handler!("The current user isn't member of the organization"),
454 };
455
456 Outcome::Success(Self {
457 host: headers.host,
458 device: headers.device,
459 user,
460 org_user_type: {
461 if let Some(org_usr_type) = UserOrgType::from_i32(org_user.atype) {
462 org_usr_type
463 } else {
464 // This should only happen if the DB is corrupted
465 err_handler!("Unknown user type in the database")
466 }
467 },
468 org_user,
469 org_id,
470 })
471 }
472 _ => err_handler!("Error getting the organization id"),
473 }
474 }
475 }
476 }
477 }
478
479 pub struct AdminHeaders {
480 pub host: String,
481 pub device: Device,
482 pub user: User,
483 pub org_user_type: UserOrgType,
484 }
485
486 impl<'a, 'r> FromRequest<'a, 'r> for AdminHeaders {
487 type Error = &'static str;
488
from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>489 fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
490 match request.guard::<OrgHeaders>() {
491 Outcome::Forward(_) => Outcome::Forward(()),
492 Outcome::Failure(f) => Outcome::Failure(f),
493 Outcome::Success(headers) => {
494 if headers.org_user_type >= UserOrgType::Admin {
495 Outcome::Success(Self {
496 host: headers.host,
497 device: headers.device,
498 user: headers.user,
499 org_user_type: headers.org_user_type,
500 })
501 } else {
502 err_handler!("You need to be Admin or Owner to call this endpoint")
503 }
504 }
505 }
506 }
507 }
508
509 impl From<AdminHeaders> for Headers {
from(h: AdminHeaders) -> Headers510 fn from(h: AdminHeaders) -> Headers {
511 Headers {
512 host: h.host,
513 device: h.device,
514 user: h.user,
515 }
516 }
517 }
518
519 // col_id is usually the fourth path param ("/organizations/<org_id>/collections/<col_id>"),
520 // but there could be cases where it is a query value.
521 // First check the path, if this is not a valid uuid, try the query values.
get_col_id(request: &Request) -> Option<String>522 fn get_col_id(request: &Request) -> Option<String> {
523 if let Some(Ok(col_id)) = request.get_param::<String>(3) {
524 if uuid::Uuid::parse_str(&col_id).is_ok() {
525 return Some(col_id);
526 }
527 }
528
529 if let Some(Ok(col_id)) = request.get_query_value::<String>("collectionId") {
530 if uuid::Uuid::parse_str(&col_id).is_ok() {
531 return Some(col_id);
532 }
533 }
534
535 None
536 }
537
538 /// The ManagerHeaders are used to check if you are at least a Manager
539 /// and have access to the specific collection provided via the <col_id>/collections/collectionId.
540 /// This does strict checking on the collection_id, ManagerHeadersLoose does not.
541 pub struct ManagerHeaders {
542 pub host: String,
543 pub device: Device,
544 pub user: User,
545 pub org_user_type: UserOrgType,
546 }
547
548 impl<'a, 'r> FromRequest<'a, 'r> for ManagerHeaders {
549 type Error = &'static str;
550
from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>551 fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
552 match request.guard::<OrgHeaders>() {
553 Outcome::Forward(_) => Outcome::Forward(()),
554 Outcome::Failure(f) => Outcome::Failure(f),
555 Outcome::Success(headers) => {
556 if headers.org_user_type >= UserOrgType::Manager {
557 match get_col_id(request) {
558 Some(col_id) => {
559 let conn = match request.guard::<DbConn>() {
560 Outcome::Success(conn) => conn,
561 _ => err_handler!("Error getting DB"),
562 };
563
564 if !headers.org_user.has_full_access() {
565 match CollectionUser::find_by_collection_and_user(
566 &col_id,
567 &headers.org_user.user_uuid,
568 &conn,
569 ) {
570 Some(_) => (),
571 None => err_handler!("The current user isn't a manager for this collection"),
572 }
573 }
574 }
575 _ => err_handler!("Error getting the collection id"),
576 }
577
578 Outcome::Success(Self {
579 host: headers.host,
580 device: headers.device,
581 user: headers.user,
582 org_user_type: headers.org_user_type,
583 })
584 } else {
585 err_handler!("You need to be a Manager, Admin or Owner to call this endpoint")
586 }
587 }
588 }
589 }
590 }
591
592 impl From<ManagerHeaders> for Headers {
from(h: ManagerHeaders) -> Headers593 fn from(h: ManagerHeaders) -> Headers {
594 Headers {
595 host: h.host,
596 device: h.device,
597 user: h.user,
598 }
599 }
600 }
601
602 /// The ManagerHeadersLoose is used when you at least need to be a Manager,
603 /// but there is no collection_id sent with the request (either in the path or as form data).
604 pub struct ManagerHeadersLoose {
605 pub host: String,
606 pub device: Device,
607 pub user: User,
608 pub org_user_type: UserOrgType,
609 }
610
611 impl<'a, 'r> FromRequest<'a, 'r> for ManagerHeadersLoose {
612 type Error = &'static str;
613
from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>614 fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
615 match request.guard::<OrgHeaders>() {
616 Outcome::Forward(_) => Outcome::Forward(()),
617 Outcome::Failure(f) => Outcome::Failure(f),
618 Outcome::Success(headers) => {
619 if headers.org_user_type >= UserOrgType::Manager {
620 Outcome::Success(Self {
621 host: headers.host,
622 device: headers.device,
623 user: headers.user,
624 org_user_type: headers.org_user_type,
625 })
626 } else {
627 err_handler!("You need to be a Manager, Admin or Owner to call this endpoint")
628 }
629 }
630 }
631 }
632 }
633
634 impl From<ManagerHeadersLoose> for Headers {
from(h: ManagerHeadersLoose) -> Headers635 fn from(h: ManagerHeadersLoose) -> Headers {
636 Headers {
637 host: h.host,
638 device: h.device,
639 user: h.user,
640 }
641 }
642 }
643
644 pub struct OwnerHeaders {
645 pub host: String,
646 pub device: Device,
647 pub user: User,
648 }
649
650 impl<'a, 'r> FromRequest<'a, 'r> for OwnerHeaders {
651 type Error = &'static str;
652
from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>653 fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
654 match request.guard::<OrgHeaders>() {
655 Outcome::Forward(_) => Outcome::Forward(()),
656 Outcome::Failure(f) => Outcome::Failure(f),
657 Outcome::Success(headers) => {
658 if headers.org_user_type == UserOrgType::Owner {
659 Outcome::Success(Self {
660 host: headers.host,
661 device: headers.device,
662 user: headers.user,
663 })
664 } else {
665 err_handler!("You need to be Owner to call this endpoint")
666 }
667 }
668 }
669 }
670 }
671
672 //
673 // Client IP address detection
674 //
675 use std::net::IpAddr;
676
677 pub struct ClientIp {
678 pub ip: IpAddr,
679 }
680
681 impl<'a, 'r> FromRequest<'a, 'r> for ClientIp {
682 type Error = ();
683
from_request(req: &'a Request<'r>) -> Outcome<Self, Self::Error>684 fn from_request(req: &'a Request<'r>) -> Outcome<Self, Self::Error> {
685 let ip = if CONFIG._ip_header_enabled() {
686 req.headers().get_one(&CONFIG.ip_header()).and_then(|ip| {
687 match ip.find(',') {
688 Some(idx) => &ip[..idx],
689 None => ip,
690 }
691 .parse()
692 .map_err(|_| warn!("'{}' header is malformed: {}", CONFIG.ip_header(), ip))
693 .ok()
694 })
695 } else {
696 None
697 };
698
699 let ip = ip.or_else(|| req.remote().map(|r| r.ip())).unwrap_or_else(|| "0.0.0.0".parse().unwrap());
700
701 Outcome::Success(ClientIp {
702 ip,
703 })
704 }
705 }
706