actix / actix-extras
1
use std::{rc::Rc, time::SystemTime};
2

3
use futures_util::future::{ready, Ready};
4
use serde::{Deserialize, Serialize};
5
use time::Duration;
6

7
use actix_web::{
8
    cookie::{Cookie, CookieJar, Key, SameSite},
9
    dev::{ServiceRequest, ServiceResponse},
10
    error::{Error, Result},
11
    http::header::{self, HeaderValue},
12
    HttpMessage,
13
};
14

15
use crate::IdentityPolicy;
16

17
struct CookieIdentityInner {
18
    key: Key,
19
    key_v2: Key,
20
    name: String,
21
    path: String,
22
    domain: Option<String>,
23
    secure: bool,
24
    max_age: Option<Duration>,
25
    http_only: Option<bool>,
26
    same_site: Option<SameSite>,
27
    visit_deadline: Option<Duration>,
28
    login_deadline: Option<Duration>,
29
}
30

31
#[derive(Debug, Deserialize, Serialize)]
32
struct CookieValue {
33
    identity: String,
34

35
    #[serde(skip_serializing_if = "Option::is_none")]
36
    login_timestamp: Option<SystemTime>,
37

38
    #[serde(skip_serializing_if = "Option::is_none")]
39
    visit_timestamp: Option<SystemTime>,
40
}
41

42
#[derive(Debug)]
43
struct CookieIdentityExtension {
44
    login_timestamp: Option<SystemTime>,
45
}
46

47
impl CookieIdentityInner {
48 1
    fn new(key: &[u8]) -> CookieIdentityInner {
49 1
        let key_v2: Vec<u8> = [key, &[1, 0, 0, 0]].concat();
50

51
        CookieIdentityInner {
52 1
            key: Key::derive_from(key),
53 1
            key_v2: Key::derive_from(&key_v2),
54 1
            name: "actix-identity".to_owned(),
55 1
            path: "/".to_owned(),
56
            domain: None,
57
            secure: true,
58
            max_age: None,
59
            http_only: None,
60
            same_site: None,
61
            visit_deadline: None,
62
            login_deadline: None,
63
        }
64
    }
65

66 1
    fn set_cookie<B>(
67
        &self,
68
        resp: &mut ServiceResponse<B>,
69
        value: Option<CookieValue>,
70
    ) -> Result<()> {
71 1
        let add_cookie = value.is_some();
72 1
        let val = value
73 1
            .map(|val| {
74 1
                if !self.legacy_supported() {
75 1
                    serde_json::to_string(&val)
76
                } else {
77 1
                    Ok(val.identity)
78
                }
79
            })
80 1
            .transpose()?;
81

82 1
        let mut cookie = Cookie::new(self.name.clone(), val.unwrap_or_default());
83 1
        cookie.set_path(self.path.clone());
84 1
        cookie.set_secure(self.secure);
85 1
        cookie.set_http_only(true);
86

87 1
        if let Some(ref domain) = self.domain {
88 1
            cookie.set_domain(domain.clone());
89
        }
90

91 1
        if let Some(max_age) = self.max_age {
92 1
            cookie.set_max_age(max_age);
93
        }
94

95 1
        if let Some(http_only) = self.http_only {
96 1
            cookie.set_http_only(http_only);
97
        }
98

99 1
        if let Some(same_site) = self.same_site {
100 1
            cookie.set_same_site(same_site);
101
        }
102

103 1
        let mut jar = CookieJar::new();
104

105 1
        let key = if self.legacy_supported() {
106 1
            &self.key
107
        } else {
108 1
            &self.key_v2
109
        };
110

111 1
        if add_cookie {
112 1
            jar.private_mut(&key).add(cookie);
113
        } else {
114 1
            jar.add_original(cookie.clone());
115 1
            jar.private_mut(&key).remove(cookie);
116
        }
117

118 1
        for cookie in jar.delta() {
119 1
            let val = HeaderValue::from_str(&cookie.to_string())?;
120 1
            resp.headers_mut().append(header::SET_COOKIE, val);
121
        }
122

123 1
        Ok(())
124
    }
125

126 1
    fn load(&self, req: &ServiceRequest) -> Option<CookieValue> {
127 1
        let cookie = req.cookie(&self.name)?;
128 1
        let mut jar = CookieJar::new();
129 1
        jar.add_original(cookie.clone());
130

131 1
        let res = if self.legacy_supported() {
132 1
            jar.private_mut(&self.key)
133 1
                .get(&self.name)
134 1
                .map(|n| CookieValue {
135 1
                    identity: n.value().to_string(),
136 1
                    login_timestamp: None,
137 1
                    visit_timestamp: None,
138
                })
139
        } else {
140 1
            None
141
        };
142

143 1
        res.or_else(|| {
144 1
            jar.private_mut(&self.key_v2)
145 1
                .get(&self.name)
146 1
                .and_then(|c| self.parse(c))
147
        })
148
    }
149

150 1
    fn parse(&self, cookie: Cookie<'_>) -> Option<CookieValue> {
151 1
        let value: CookieValue = serde_json::from_str(cookie.value()).ok()?;
152 1
        let now = SystemTime::now();
153

154 1
        if let Some(visit_deadline) = self.visit_deadline {
155 1
            let inactivity = now.duration_since(value.visit_timestamp?).ok()?;
156

157 1
            if inactivity > visit_deadline {
158 1
                return None;
159
            }
160
        }
161

162 1
        if let Some(login_deadline) = self.login_deadline {
163 1
            let logged_in_dur = now.duration_since(value.login_timestamp?).ok()?;
164

165 1
            if logged_in_dur > login_deadline {
166 1
                return None;
167
            }
168
        }
169

170 1
        Some(value)
171
    }
172

173 1
    fn legacy_supported(&self) -> bool {
174 1
        self.visit_deadline.is_none() && self.login_deadline.is_none()
175
    }
176

177 1
    fn always_update_cookie(&self) -> bool {
178 1
        self.visit_deadline.is_some()
179
    }
180

181 1
    fn requires_oob_data(&self) -> bool {
182 1
        self.login_deadline.is_some()
183
    }
184
}
185

186
/// Use cookies for request identity storage.
187
///
188
/// [See this page on MDN](mdn-cookies) for details on cookie attributes.
189
///
190
/// # Examples
191
/// ```
192
/// use actix_web::App;
193
/// use actix_identity::{CookieIdentityPolicy, IdentityService};
194
///
195
/// // create cookie identity backend
196
/// let policy = CookieIdentityPolicy::new(&[0; 32])
197
///            .domain("www.rust-lang.org")
198
///            .name("actix_auth")
199
///            .path("/")
200
///            .secure(true);
201
///
202
/// let app = App::new()
203
///     // wrap policy into identity middleware
204
///     .wrap(IdentityService::new(policy));
205
/// ```
206
///
207
/// [mdn-cookies]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
208
pub struct CookieIdentityPolicy(Rc<CookieIdentityInner>);
209

210
impl CookieIdentityPolicy {
211
    /// Create new `CookieIdentityPolicy` instance.
212
    ///
213
    /// Key argument is the private key for issued cookies. If this value is changed, all issued
214
    /// cookie identities are invalidated.
215
    ///
216
    /// # Panics
217
    /// Panics if `key` is less than 32 bytes in length..
218 1
    pub fn new(key: &[u8]) -> CookieIdentityPolicy {
219 1
        CookieIdentityPolicy(Rc::new(CookieIdentityInner::new(key)))
220
    }
221

222
    /// Sets the name of issued cookies.
223 1
    pub fn name(mut self, value: impl Into<String>) -> CookieIdentityPolicy {
224 1
        self.inner_mut().name = value.into();
225 1
        self
226
    }
227

228
    /// Sets the `Path` attribute of issued cookies.
229 1
    pub fn path(mut self, value: impl Into<String>) -> CookieIdentityPolicy {
230 1
        self.inner_mut().path = value.into();
231 1
        self
232
    }
233

234
    /// Sets the `Domain` attribute of issued cookies.
235 1
    pub fn domain(mut self, value: impl Into<String>) -> CookieIdentityPolicy {
236 1
        self.inner_mut().domain = Some(value.into());
237 1
        self
238
    }
239

240
    /// Sets the `Secure` attribute of issued cookies.
241 1
    pub fn secure(mut self, value: bool) -> CookieIdentityPolicy {
242 1
        self.inner_mut().secure = value;
243 1
        self
244
    }
245

246
    /// Sets the `Max-Age` attribute of issued cookies.
247 1
    pub fn max_age(mut self, value: Duration) -> CookieIdentityPolicy {
248 1
        self.inner_mut().max_age = Some(value);
249 1
        self
250
    }
251

252
    /// Sets the `Max-Age` attribute of issued cookies with given number of seconds.
253 1
    pub fn max_age_secs(self, seconds: i64) -> CookieIdentityPolicy {
254 1
        self.max_age(Duration::seconds(seconds))
255
    }
256

257
    /// Sets the `HttpOnly` attribute of issued cookies.
258
    ///
259
    /// By default, the `HttpOnly` attribute is omitted from issued cookies.
260 1
    pub fn http_only(mut self, http_only: bool) -> Self {
261 1
        self.inner_mut().http_only = Some(http_only);
262 1
        self
263
    }
264

265
    /// Sets the `SameSite` attribute of issued cookies.
266
    ///
267
    /// By default, the `SameSite` attribute is omitted from issued cookies.
268 1
    pub fn same_site(mut self, same_site: SameSite) -> Self {
269 1
        self.inner_mut().same_site = Some(same_site);
270 1
        self
271
    }
272

273
    /// Accepts only users who have visited within given deadline.
274
    ///
275
    /// In other words, invalidate a login after some amount of inactivity. Using this feature
276
    /// causes updated cookies to be issued on each response in order to record the user's last
277
    /// visitation timestamp.
278
    ///
279
    /// By default, visit deadline is disabled.
280 1
    pub fn visit_deadline(mut self, deadline: Duration) -> CookieIdentityPolicy {
281 1
        self.inner_mut().visit_deadline = Some(deadline);
282 1
        self
283
    }
284

285
    /// Accepts only users who authenticated within the given deadline.
286
    ///
287
    /// In other words, invalidate a login after some amount of time, regardless of activity.
288
    /// While [`Max-Age`](CookieIdentityPolicy::max_age) is useful in constraining the cookie
289
    /// lifetime, it could be extended manually; using this feature encodes the deadline directly
290
    /// into the issued cookies, making it immutable to users.
291
    ///
292
    /// By default, login deadline is disabled.
293 1
    pub fn login_deadline(mut self, deadline: Duration) -> CookieIdentityPolicy {
294 1
        self.inner_mut().login_deadline = Some(deadline);
295 1
        self
296
    }
297

298 1
    fn inner_mut(&mut self) -> &mut CookieIdentityInner {
299 1
        Rc::get_mut(&mut self.0).unwrap()
300
    }
301
}
302

303
impl IdentityPolicy for CookieIdentityPolicy {
304
    type Future = Ready<Result<Option<String>, Error>>;
305
    type ResponseFuture = Ready<Result<(), Error>>;
306

307 1
    fn from_request(&self, req: &mut ServiceRequest) -> Self::Future {
308 1
        ready(Ok(self.0.load(req).map(|value| {
309 0
            let CookieValue {
310 1
                identity,
311 1
                login_timestamp,
312 0
                ..
313 0
            } = value;
314

315 1
            if self.0.requires_oob_data() {
316 1
                req.extensions_mut()
317 1
                    .insert(CookieIdentityExtension { login_timestamp });
318
            }
319

320 0
            identity
321
        })))
322
    }
323

324 1
    fn to_response<B>(
325
        &self,
326
        id: Option<String>,
327
        changed: bool,
328
        res: &mut ServiceResponse<B>,
329
    ) -> Self::ResponseFuture {
330 1
        let _ = if changed {
331 1
            let login_timestamp = SystemTime::now();
332

333 1
            self.0.set_cookie(
334 0
                res,
335 1
                id.map(|identity| CookieValue {
336 1
                    identity,
337 1
                    login_timestamp: self.0.login_deadline.map(|_| login_timestamp),
338 1
                    visit_timestamp: self.0.visit_deadline.map(|_| login_timestamp),
339
                }),
340
            )
341 1
        } else if self.0.always_update_cookie() && id.is_some() {
342 1
            let visit_timestamp = SystemTime::now();
343

344 1
            let login_timestamp = if self.0.requires_oob_data() {
345 1
                let CookieIdentityExtension { login_timestamp } =
346 0
                    res.request().extensions_mut().remove().unwrap();
347

348 1
                login_timestamp
349
            } else {
350 0
                None
351
            };
352

353 1
            self.0.set_cookie(
354 0
                res,
355 1
                Some(CookieValue {
356 1
                    identity: id.unwrap(),
357 1
                    login_timestamp,
358 1
                    visit_timestamp: self.0.visit_deadline.map(|_| visit_timestamp),
359
                }),
360
            )
361
        } else {
362 1
            Ok(())
363
        };
364

365 1
        ready(Ok(()))
366
    }
367
}
368

369
#[cfg(test)]
370
mod tests {
371
    use std::{borrow::Borrow, time::SystemTime};
372

373
    use actix_web::{
374
        cookie::{Cookie, CookieJar, Key, SameSite},
375
        dev::ServiceResponse,
376
        http::{header, StatusCode},
377
        test::{self, TestRequest},
378
        web, App, HttpResponse,
379
    };
380
    use time::Duration;
381

382
    use super::*;
383
    use crate::{tests::*, Identity, IdentityService};
384

385 1
    fn login_cookie(
386
        identity: &'static str,
387
        login_timestamp: Option<SystemTime>,
388
        visit_timestamp: Option<SystemTime>,
389
    ) -> Cookie<'static> {
390 1
        let mut jar = CookieJar::new();
391 1
        let key: Vec<u8> = COOKIE_KEY_MASTER
392
            .iter()
393 1
            .chain([1, 0, 0, 0].iter())
394
            .copied()
395
            .collect();
396

397 1
        jar.private_mut(&Key::derive_from(&key)).add(Cookie::new(
398
            COOKIE_NAME,
399 1
            serde_json::to_string(&CookieValue {
400 1
                identity: identity.to_string(),
401 1
                login_timestamp,
402 1
                visit_timestamp,
403
            })
404
            .unwrap(),
405
        ));
406

407 1
        jar.get(COOKIE_NAME).unwrap().clone()
408
    }
409

410 1
    fn assert_login_cookie(
411
        response: &mut ServiceResponse,
412
        identity: &str,
413
        login_timestamp: LoginTimestampCheck,
414
        visit_timestamp: VisitTimeStampCheck,
415
    ) {
416 1
        let mut cookies = CookieJar::new();
417

418 1
        for cookie in response.headers().get_all(header::SET_COOKIE) {
419 1
            cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
420
        }
421

422 1
        let key: Vec<u8> = COOKIE_KEY_MASTER
423
            .iter()
424 1
            .chain([1, 0, 0, 0].iter())
425
            .copied()
426
            .collect();
427

428 1
        let cookie = cookies
429 1
            .private(&Key::derive_from(&key))
430
            .get(COOKIE_NAME)
431
            .unwrap();
432

433 1
        let cv: CookieValue = serde_json::from_str(cookie.value()).unwrap();
434 1
        assert_eq!(cv.identity, identity);
435

436 1
        let now = SystemTime::now();
437 1
        let t30sec_ago = now - Duration::seconds(30);
438

439 1
        match login_timestamp {
440 1
            LoginTimestampCheck::NoTimestamp => assert_eq!(cv.login_timestamp, None),
441 1
            LoginTimestampCheck::NewTimestamp => assert!(
442 1
                t30sec_ago <= cv.login_timestamp.unwrap()
443 1
                    && cv.login_timestamp.unwrap() <= now
444
            ),
445 1
            LoginTimestampCheck::OldTimestamp(old_timestamp) => {
446 1
                assert_eq!(cv.login_timestamp, Some(old_timestamp))
447
            }
448
        }
449

450 1
        match visit_timestamp {
451 1
            VisitTimeStampCheck::NoTimestamp => assert_eq!(cv.visit_timestamp, None),
452 1
            VisitTimeStampCheck::NewTimestamp => assert!(
453 1
                t30sec_ago <= cv.visit_timestamp.unwrap()
454 1
                    && cv.visit_timestamp.unwrap() <= now
455
            ),
456
        }
457
    }
458

459 1
    #[actix_rt::test]
460 1
    async fn test_identity_flow() {
461 1
        let srv = test::init_service(
462 1
            App::new()
463 1
                .wrap(IdentityService::new(
464 1
                    CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
465
                        .domain("www.rust-lang.org")
466
                        .name(COOKIE_NAME)
467
                        .path("/")
468
                        .secure(true),
469
                ))
470 1
                .service(web::resource("/index").to(|id: Identity| {
471 1
                    if id.identity().is_some() {
472 1
                        HttpResponse::Created()
473
                    } else {
474 1
                        HttpResponse::Ok()
475
                    }
476
                }))
477 1
                .service(web::resource("/login").to(|id: Identity| {
478 1
                    id.remember(COOKIE_LOGIN.to_string());
479 1
                    HttpResponse::Ok()
480
                }))
481 1
                .service(web::resource("/logout").to(|id: Identity| {
482 1
                    if id.identity().is_some() {
483 1
                        id.forget();
484 1
                        HttpResponse::Ok()
485
                    } else {
486 0
                        HttpResponse::BadRequest()
487
                    }
488
                })),
489
        )
490
        .await;
491 1
        let resp =
492
            test::call_service(&srv, TestRequest::with_uri("/index").to_request()).await;
493 1
        assert_eq!(resp.status(), StatusCode::OK);
494

495 1
        let resp =
496
            test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
497 1
        assert_eq!(resp.status(), StatusCode::OK);
498 1
        let c = resp.response().cookies().next().unwrap().to_owned();
499

500 1
        let resp = test::call_service(
501
            &srv,
502 1
            TestRequest::with_uri("/index")
503 1
                .cookie(c.clone())
504
                .to_request(),
505
        )
506
        .await;
507 1
        assert_eq!(resp.status(), StatusCode::CREATED);
508

509 1
        let resp = test::call_service(
510
            &srv,
511 1
            TestRequest::with_uri("/logout")
512 1
                .cookie(c.clone())
513
                .to_request(),
514
        )
515
        .await;
516 1
        assert_eq!(resp.status(), StatusCode::OK);
517 1
        assert!(resp.headers().contains_key(header::SET_COOKIE))
518
    }
519

520 1
    #[actix_rt::test]
521 1
    async fn test_identity_max_age_time() {
522 1
        let duration = Duration::days(1);
523

524 1
        let srv = test::init_service(
525 1
            App::new()
526 1
                .wrap(IdentityService::new(
527 1
                    CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
528
                        .domain("www.rust-lang.org")
529
                        .name(COOKIE_NAME)
530
                        .path("/")
531 1
                        .max_age(duration)
532
                        .secure(true),
533
                ))
534 1
                .service(web::resource("/login").to(|id: Identity| {
535 1
                    id.remember("test".to_string());
536 1
                    HttpResponse::Ok()
537
                })),
538
        )
539
        .await;
540

541 1
        let resp =
542
            test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
543 1
        assert_eq!(resp.status(), StatusCode::OK);
544 1
        assert!(resp.headers().contains_key(header::SET_COOKIE));
545 1
        let c = resp.response().cookies().next().unwrap().to_owned();
546 1
        assert_eq!(duration, c.max_age().unwrap());
547
    }
548

549 1
    #[actix_rt::test]
550 1
    async fn test_http_only_same_site() {
551 1
        let srv = test::init_service(
552 1
            App::new()
553 1
                .wrap(IdentityService::new(
554 1
                    CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
555
                        .domain("www.rust-lang.org")
556
                        .name(COOKIE_NAME)
557
                        .path("/")
558
                        .http_only(true)
559 1
                        .same_site(SameSite::None),
560
                ))
561 1
                .service(web::resource("/login").to(|id: Identity| {
562 1
                    id.remember("test".to_string());
563 1
                    HttpResponse::Ok()
564
                })),
565
        )
566
        .await;
567

568 1
        let resp =
569
            test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
570

571 1
        assert_eq!(resp.status(), StatusCode::OK);
572 1
        assert!(resp.headers().contains_key(header::SET_COOKIE));
573

574 1
        let c = resp.response().cookies().next().unwrap().to_owned();
575 1
        assert!(c.http_only().unwrap());
576 1
        assert_eq!(SameSite::None, c.same_site().unwrap());
577
    }
578

579 1
    fn legacy_login_cookie(identity: &'static str) -> Cookie<'static> {
580 1
        let mut jar = CookieJar::new();
581 1
        jar.private_mut(&Key::derive_from(&COOKIE_KEY_MASTER))
582 1
            .add(Cookie::new(COOKIE_NAME, identity));
583 1
        jar.get(COOKIE_NAME).unwrap().clone()
584
    }
585

586 1
    async fn assert_logged_in(response: ServiceResponse, identity: Option<&str>) {
587 1
        let bytes = test::read_body(response).await;
588 1
        let resp: Option<String> = serde_json::from_slice(&bytes[..]).unwrap();
589 1
        assert_eq!(resp.as_ref().map(|s| s.borrow()), identity);
590
    }
591

592 1
    fn assert_legacy_login_cookie(response: &mut ServiceResponse, identity: &str) {
593 1
        let mut cookies = CookieJar::new();
594 1
        for cookie in response.headers().get_all(header::SET_COOKIE) {
595 1
            cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
596
        }
597 1
        let cookie = cookies
598 1
            .private_mut(&Key::derive_from(&COOKIE_KEY_MASTER))
599
            .get(COOKIE_NAME)
600
            .unwrap();
601 1
        assert_eq!(cookie.value(), identity);
602
    }
603

604 1
    fn assert_no_login_cookie(response: &mut ServiceResponse) {
605 1
        let mut cookies = CookieJar::new();
606 1
        for cookie in response.headers().get_all(header::SET_COOKIE) {
607 0
            cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
608
        }
609 1
        assert!(cookies.get(COOKIE_NAME).is_none());
610
    }
611

612 1
    #[actix_rt::test]
613 1
    async fn test_identity_max_age() {
614 1
        let seconds = 60;
615 1
        let srv = test::init_service(
616 1
            App::new()
617 1
                .wrap(IdentityService::new(
618 1
                    CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
619
                        .domain("www.rust-lang.org")
620
                        .name(COOKIE_NAME)
621
                        .path("/")
622 1
                        .max_age_secs(seconds)
623
                        .secure(true),
624
                ))
625 1
                .service(web::resource("/login").to(|id: Identity| {
626 1
                    id.remember("test".to_string());
627 1
                    HttpResponse::Ok()
628
                })),
629
        )
630
        .await;
631 1
        let resp =
632
            test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
633 1
        assert_eq!(resp.status(), StatusCode::OK);
634 1
        assert!(resp.headers().contains_key(header::SET_COOKIE));
635 1
        let c = resp.response().cookies().next().unwrap().to_owned();
636 1
        assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap());
637
    }
638

639 1
    #[actix_rt::test]
640 1
    async fn test_identity_legacy_cookie_is_set() {
641 1
        let srv = create_identity_server(|c| c).await;
642 1
        let mut resp =
643
            test::call_service(&srv, TestRequest::with_uri("/").to_request()).await;
644 1
        assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN);
645 1
        assert_logged_in(resp, None).await;
646
    }
647

648 1
    #[actix_rt::test]
649 1
    async fn test_identity_legacy_cookie_works() {
650 1
        let srv = create_identity_server(|c| c).await;
651 1
        let cookie = legacy_login_cookie(COOKIE_LOGIN);
652 1
        let mut resp = test::call_service(
653
            &srv,
654 1
            TestRequest::with_uri("/")
655 1
                .cookie(cookie.clone())
656
                .to_request(),
657
        )
658
        .await;
659 1
        assert_no_login_cookie(&mut resp);
660 1
        assert_logged_in(resp, Some(COOKIE_LOGIN)).await;
661
    }
662

663 1
    #[actix_rt::test]
664 1
    async fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() {
665 1
        let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
666 1
        let cookie = legacy_login_cookie(COOKIE_LOGIN);
667 1
        let mut resp = test::call_service(
668
            &srv,
669 1
            TestRequest::with_uri("/")
670 1
                .cookie(cookie.clone())
671
                .to_request(),
672
        )
673
        .await;
674
        assert_login_cookie(
675 1
            &mut resp,
676
            COOKIE_LOGIN,
677 1
            LoginTimestampCheck::NoTimestamp,
678 1
            VisitTimeStampCheck::NewTimestamp,
679
        );
680 1
        assert_logged_in(resp, None).await;
681
    }
682

683 1
    #[actix_rt::test]
684 1
    async fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() {
685 1
        let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
686 1
        let cookie = legacy_login_cookie(COOKIE_LOGIN);
687 1
        let mut resp = test::call_service(
688
            &srv,
689 1
            TestRequest::with_uri("/")
690 1
                .cookie(cookie.clone())
691
                .to_request(),
692
        )
693
        .await;
694
        assert_login_cookie(
695 1
            &mut resp,
696
            COOKIE_LOGIN,
697 1
            LoginTimestampCheck::NewTimestamp,
698 1
            VisitTimeStampCheck::NoTimestamp,
699
        );
700 1
        assert_logged_in(resp, None).await;
701
    }
702

703 1
    #[actix_rt::test]
704 1
    async fn test_identity_cookie_rejected_if_login_timestamp_needed() {
705 1
        let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
706 1
        let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now()));
707 1
        let mut resp = test::call_service(
708
            &srv,
709 1
            TestRequest::with_uri("/")
710 1
                .cookie(cookie.clone())
711
                .to_request(),
712
        )
713
        .await;
714
        assert_login_cookie(
715 1
            &mut resp,
716
            COOKIE_LOGIN,
717 1
            LoginTimestampCheck::NewTimestamp,
718 1
            VisitTimeStampCheck::NoTimestamp,
719
        );
720 1
        assert_logged_in(resp, None).await;
721
    }
722

723 1
    #[actix_rt::test]
724 1
    async fn test_identity_cookie_rejected_if_visit_timestamp_needed() {
725 1
        let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
726 1
        let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
727 1
        let mut resp = test::call_service(
728
            &srv,
729 1
            TestRequest::with_uri("/")
730 1
                .cookie(cookie.clone())
731
                .to_request(),
732
        )
733
        .await;
734
        assert_login_cookie(
735 1
            &mut resp,
736
            COOKIE_LOGIN,
737 1
            LoginTimestampCheck::NoTimestamp,
738 1
            VisitTimeStampCheck::NewTimestamp,
739
        );
740 1
        assert_logged_in(resp, None).await;
741
    }
742

743 1
    #[actix_rt::test]
744 1
    async fn test_identity_cookie_rejected_if_login_timestamp_too_old() {
745 1
        let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
746
        let cookie = login_cookie(
747
            COOKIE_LOGIN,
748 1
            Some(SystemTime::now() - Duration::days(180)),
749 1
            None,
750
        );
751 1
        let mut resp = test::call_service(
752
            &srv,
753 1
            TestRequest::with_uri("/")
754 1
                .cookie(cookie.clone())
755
                .to_request(),
756
        )
757
        .await;
758
        assert_login_cookie(
759 1
            &mut resp,
760
            COOKIE_LOGIN,
761 1
            LoginTimestampCheck::NewTimestamp,
762 1
            VisitTimeStampCheck::NoTimestamp,
763
        );
764 1
        assert_logged_in(resp, None).await;
765
    }
766

767 1
    #[actix_rt::test]
768 1
    async fn test_identity_cookie_rejected_if_visit_timestamp_too_old() {
769 1
        let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
770
        let cookie = login_cookie(
771
            COOKIE_LOGIN,
772 1
            None,
773 1
            Some(SystemTime::now() - Duration::days(180)),
774
        );
775 1
        let mut resp = test::call_service(
776
            &srv,
777 1
            TestRequest::with_uri("/")
778 1
                .cookie(cookie.clone())
779
                .to_request(),
780
        )
781
        .await;
782
        assert_login_cookie(
783 1
            &mut resp,
784
            COOKIE_LOGIN,
785 1
            LoginTimestampCheck::NoTimestamp,
786 1
            VisitTimeStampCheck::NewTimestamp,
787
        );
788 1
        assert_logged_in(resp, None).await;
789
    }
790

791 1
    #[actix_rt::test]
792 1
    async fn test_identity_cookie_not_updated_on_login_deadline() {
793 1
        let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
794 1
        let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
795 1
        let mut resp = test::call_service(
796
            &srv,
797 1
            TestRequest::with_uri("/")
798 1
                .cookie(cookie.clone())
799
                .to_request(),
800
        )
801
        .await;
802 1
        assert_no_login_cookie(&mut resp);
803 1
        assert_logged_in(resp, Some(COOKIE_LOGIN)).await;
804
    }
805

806 1
    #[actix_rt::test]
807 1
    async fn test_identity_cookie_updated_on_visit_deadline() {
808 1
        let srv = create_identity_server(|c| {
809 1
            c.visit_deadline(Duration::days(90))
810 1
                .login_deadline(Duration::days(90))
811
        })
812
        .await;
813 1
        let timestamp = SystemTime::now() - Duration::days(1);
814 1
        let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp));
815 1
        let mut resp = test::call_service(
816
            &srv,
817 1
            TestRequest::with_uri("/")
818 1
                .cookie(cookie.clone())
819
                .to_request(),
820
        )
821
        .await;
822
        assert_login_cookie(
823 1
            &mut resp,
824
            COOKIE_LOGIN,
825 1
            LoginTimestampCheck::OldTimestamp(timestamp),
826 1
            VisitTimeStampCheck::NewTimestamp,
827
        );
828 1
        assert_logged_in(resp, Some(COOKIE_LOGIN)).await;
829
    }
830
}

Read our documentation on viewing source code .

Loading