main.rs 22.3 KB
Newer Older
1
2
#![cfg_attr(feature = "strict", deny(warnings))]

Robert Czechowski's avatar
Robert Czechowski committed
3
4
5
6
7
8
9
#[macro_use]
extern crate iron;
#[macro_use]
extern crate router;
#[macro_use]
extern crate serde_derive;

Robert Czechowski's avatar
Robert Czechowski committed
10
extern crate handlebars_iron;
Robert Czechowski's avatar
Robert Czechowski committed
11
12
extern crate iron_sessionstorage;
extern crate mount;
13
extern crate params;
Robert Czechowski's avatar
Robert Czechowski committed
14
extern crate persistent;
15
#[cfg(feature = "postgres")]
16
extern crate postgres;
Robert Czechowski's avatar
Robert Czechowski committed
17
extern crate rand;
18
extern crate reqwest;
19
#[cfg(feature = "rusqlite")]
Robert Czechowski's avatar
Robert Czechowski committed
20
21
extern crate rusqlite;
extern crate serde_json;
22
extern crate serde_yaml;
Robert Czechowski's avatar
Robert Czechowski committed
23
24
25
26
extern crate staticfile;
extern crate structopt;
extern crate time;
extern crate urlencoded;
27
#[cfg(feature = "webbrowser")]
28
extern crate webbrowser;
29

30
31
pub mod config;
pub mod contestreader_yaml;
32
pub mod db_conn;
33
34
35
36
pub mod functions;
pub mod oauth_provider;

mod db_apply_migrations;
37
mod db_conn_postgres;
38
mod db_conn_sqlite;
39
mod db_objects;
40
41
mod webfw_iron;

42
use db_conn::{MedalConnection, MedalObject};
43
use db_objects::*;
44
use functions::SetPassword; // TODO: Refactor, so we don't need to take this from there!
Robert Czechowski's avatar
Robert Czechowski committed
45
46
use webfw_iron::start_server;

47
use config::Config;
48
49
use structopt::StructOpt;

50
use std::path::{Path, PathBuf};
51

52
fn read_contest(p: &PathBuf) -> Option<Contest> {
53
54
    use std::fs::File;
    use std::io::Read;
55

56
57
58
    let mut file = File::open(p).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
59

60
    contestreader_yaml::parse_yaml(&contents,
61
62
                                   p.file_name().to_owned()?.to_str()?,
                                   &format!("{}/", p.parent().unwrap().to_str()?))
Robert Czechowski's avatar
Robert Czechowski committed
63
64
65
}

fn get_all_contest_info(task_dir: &str) -> Vec<Contest> {
66
67
    fn walk_me_recursively(p: &PathBuf, contests: &mut Vec<Contest>) {
        if let Ok(paths) = std::fs::read_dir(p) {
68
            for path in paths {
Robert Czechowski's avatar
Robert Czechowski committed
69
70
                let p = path.unwrap().path();
                walk_me_recursively(&p, contests);
71
            }
Robert Czechowski's avatar
Robert Czechowski committed
72
        }
73

74
        if p.file_name().unwrap().to_string_lossy().to_string().ends_with(".yaml") {
75
            read_contest(p).map(|contest| contests.push(contest));
76
        };
Robert Czechowski's avatar
Robert Czechowski committed
77
78
79
    };

    let mut contests = Vec::new();
80
    match std::fs::read_dir(task_dir) {
Robert Czechowski's avatar
Robert Czechowski committed
81
        Err(why) => println!("Error opening tasks directory! {:?}", why.kind()),
Robert Czechowski's avatar
Robert Czechowski committed
82
83
84
85
86
        Ok(paths) => {
            for path in paths {
                walk_me_recursively(&path.unwrap().path(), &mut contests);
            }
        }
Robert Czechowski's avatar
Robert Czechowski committed
87
88
89
90
91
    };

    contests
}

92
93
94
95
fn refresh_all_contests<C>(conn: &mut C)
    where C: MedalConnection,
          db_objects::Contest: db_conn::MedalObject<C>
{
Robert Czechowski's avatar
Robert Czechowski committed
96
97
98
99
100
101
102
    let v = get_all_contest_info("tasks/");

    for mut contest_info in v {
        contest_info.save(conn);
    }
}

103
104
fn add_admin_user<C>(conn: &mut C, resetpw: bool)
    where C: MedalConnection {
105
106
107
    let mut admin = match conn.get_user_by_id(1) {
        None => {
            print!("New Database. Creating new admin user with credentials 'admin':");
108
            conn.new_session("")
Robert Czechowski's avatar
Robert Czechowski committed
109
        }
110
111
        Some(user) => {
            if !resetpw {
Robert Czechowski's avatar
Robert Czechowski committed
112
                return;
113
            }
114
115
116
117
118
            print!("Request to reset admin password. Set credentials 'admin':");
            user
        }
    };

Robert Czechowski's avatar
Robert Czechowski committed
119
    use rand::{distributions::Alphanumeric, thread_rng, Rng};
120
121

    let password: String = thread_rng().sample_iter(&Alphanumeric)
Robert Czechowski's avatar
Robert Czechowski committed
122
123
124
125
126
127
                                       .filter(|x| {
                                           let x = *x;
                                           !(x == 'l' || x == 'I' || x == '1' || x == 'O' || x == 'o' || x == '0')
                                       })
                                       .take(8)
                                       .collect();
128
129
130
131
    print!("'{}' …", &password);

    admin.username = Some("admin".into());
    match admin.set_password(&password) {
132
        None => println!(" FAILED! (Password hashing error)"),
133
134
        _ => {
            conn.save_session(admin);
135
            println!(" Done");
136
        }
137
138
139
    }
}

140
141
142
143
144
145
146
fn prepare_and_start_server<C>(mut conn: C, config: Config, onlycontestscan: bool, resetadminpw: bool)
    where C: MedalConnection + std::marker::Send + 'static,
          db_objects::Contest: db_conn::MedalObject<C>
{
    db_apply_migrations::test(&mut conn);

    if onlycontestscan || config.no_contest_scan == Some(false) {
147
        print!("Scanning for contests …");
148
        refresh_all_contests(&mut conn);
149
        println!(" Done")
150
151
152
153
154
    }

    if !onlycontestscan {
        add_admin_user(&mut conn, resetadminpw);

155
        #[cfg(feature = "webbrowser")]
156
        let self_url = config.self_url.clone();
157
        #[cfg(feature = "webbrowser")]
158
159
        let open_browser = config.open_browser;

160
        match start_server(conn, config) {
161
162
            Ok(_) => {
                println!("Server started");
163

164
165
166
167
168
                #[cfg(feature = "webbrowser")]
                {
                    if let (Some(self_url), Some(true)) = (self_url, open_browser) {
                        open_browser_window(&self_url);
                    }
169
                }
170
            }
171
172
            Err(_) => println!("Error on server start …"),
        };
173

174
175
176
177
        println!("Could not run server. Is the port already in use?");
    }
}

178
#[cfg(feature = "webbrowser")]
179
180
181
fn open_browser_window(self_url: &str) {
    match webbrowser::open(&self_url) {
        Ok(_) => (),
182
        Err(e) => println!("Error while opening webbrowser: {:?}", e),
183
184
185
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
186
fn main() {
187
    let opt = config::Opt::from_args();
188
    //println!("{:?}", opt); // Show in different debug level?
Daniel Brüning's avatar
Daniel Brüning committed
189

190
    let mut config = config::read_config_from_file(&opt.configfile);
191

Robert Czechowski's avatar
Robert Czechowski committed
192
193
194
    if opt.databasefile.is_some() {
        config.database_file = opt.databasefile;
    }
195
196
197
    if config.database_file.is_none() {
        config.database_file = Some(Path::new("medal.db").to_owned())
    }
198
199
200
    if opt.databaseurl.is_some() {
        config.database_url = opt.databaseurl;
    }
Robert Czechowski's avatar
Robert Czechowski committed
201
202
203
    if opt.port.is_some() {
        config.port = opt.port;
    }
204
205
206
    if opt.nocontestscan {
        config.no_contest_scan = Some(true);
    }
207
208
209
    if opt.openbrowser {
        config.open_browser = Some(true)
    }
210

211
212
213
214
215
216
217
218
219
220
    #[cfg(feature = "postgres")]
    {
        if let Some(url) = config.database_url.clone() {
            print!("Using database {} … ", &url);
            let conn = postgres::Connection::connect(url, postgres::TlsMode::None).unwrap();
            println!("Connected");

            prepare_and_start_server(conn, config, opt.onlycontestscan, opt.resetadminpw);
            return;
        }
221
    }
222
223
224
225
226
227
228
229
230
231
232
233
234
235

    #[cfg(feature = "rusqlite")]
    {
        if let Some(path) = config.database_file.clone() {
            print!("Using database file {} … ", &path.to_str().unwrap_or("<unprintable filename>"));
            let conn = rusqlite::Connection::open(path).unwrap();
            println!("Connected");

            prepare_and_start_server(conn, config, opt.onlycontestscan, opt.resetadminpw);
            return;
        }
    }

    println!("No database configured. Try enableing the 'rusqlite' feature during compilation.\nLeaving now.");
236
}
237
238
239
240

#[cfg(test)]
mod tests {
    use super::*;
Robert Czechowski's avatar
Robert Czechowski committed
241
    use reqwest::StatusCode;
242

Robert Czechowski's avatar
Robert Czechowski committed
243
244
    fn start_server_and_fn<F>(port: u16, set_user: Option<(String, String, bool)>, f: F)
        where F: Fn() {
245
        use std::sync::mpsc::channel;
Robert Czechowski's avatar
Robert Czechowski committed
246
        use std::{thread, time};
247
248
249
250
        let (start_tx, start_rx) = channel();
        let (stop_tx, stop_rx) = channel();

        thread::spawn(move || {
251
            let mut conn = rusqlite::Connection::open_in_memory().unwrap();
252
253
            db_apply_migrations::test(&mut conn);

254
            if let Some(user) = set_user {
255
                let mut test_user = conn.new_session("");
256
                test_user.username = Some(user.0);
Robert Czechowski's avatar
Robert Czechowski committed
257
258
259
                test_user.is_teacher = user.2;
                test_user.set_password(&user.1).expect("Set Password did not work correctly.");
                conn.save_session(test_user);
260
261
            }

Robert Czechowski's avatar
Robert Czechowski committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
            let mut contest = Contest::new("directory".to_string(),
                                           "public.yaml".to_string(),
                                           "RenamedContestName".to_string(),
                                           1,
                                           true,
                                           None,
                                           None);
            contest.save(&conn);

            let mut contest = Contest::new("directory".to_string(),
                                           "public.yaml".to_string(),
                                           "PublicContestName".to_string(),
                                           1,
                                           true,
                                           None,
                                           None);
            let mut taskgroup = Taskgroup::new("TaksgroupName".to_string());
            let task = Task::new("taskdir1".to_string(), 3);
            taskgroup.tasks.push(task);
            let task = Task::new("taskdir2".to_string(), 4);
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

            let mut contest = Contest::new("directory".to_string(),
                                           "private.yaml".to_string(),
                                           "PrivateContestName".to_string(),
                                           1,
                                           false,
                                           None,
                                           None);
            let mut taskgroup = Taskgroup::new("TaksgroupName".to_string());
            let task = Task::new("taskdir1".to_string(), 3);
            taskgroup.tasks.push(task);
            let task = Task::new("taskdir2".to_string(), 4);
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

            let mut contest = Contest::new("directory".to_string(),
                                           "infinte.yaml".to_string(),
                                           "InfiniteContestName".to_string(),
                                           0,
                                           true,
                                           None,
                                           None);
            let mut taskgroup = Taskgroup::new("TaksgroupName".to_string());
            let task = Task::new("taskdir1".to_string(), 3);
            taskgroup.tasks.push(task);
            let task = Task::new("taskdir2".to_string(), 4);
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

316
            let mut config = config::read_config_from_file(Path::new("thisfileshoudnotexist"));
317
318
319
            config.port = Some(port);
            let srvr = start_server(conn, config);

320
            // Message server started
321
322
            start_tx.send(()).unwrap();

323
            // Wait for test to finish
324
325
326
327
328
            stop_rx.recv().unwrap();

            srvr.unwrap().close().unwrap();
        });

329
        // Wait for server to start
330
331
332
        start_rx.recv().unwrap();
        thread::sleep(time::Duration::from_millis(100));
        f();
333
        // Message test finished
334
335
336
        stop_tx.send(()).unwrap();
    }

337
    fn login(port: u16, client: &reqwest::Client, username: &str, password: &str) -> reqwest::Response {
338
        let params = [("username", username), ("password", password)];
Robert Czechowski's avatar
Robert Czechowski committed
339
        let resp = client.post(&format!("http://localhost:{}/login", port)).form(&params).send().unwrap();
340
        resp
341
    }
Robert Czechowski's avatar
Robert Czechowski committed
342

343
344
345
346
347
    fn login_code(port: u16, client: &reqwest::Client, code: &str) -> reqwest::Response {
        let params = [("code", code)];
        let resp = client.post(&format!("http://localhost:{}/clogin", port)).form(&params).send().unwrap();
        resp
    }
348

349
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
350
    fn start_server_and_check_requests() {
Robert Czechowski's avatar
Robert Czechowski committed
351
        start_server_and_fn(8080, None, || {
352
            let mut resp = reqwest::get("http://localhost:8080").unwrap();
353
            assert_eq!(resp.status(), StatusCode::OK);
354
355

            let content = resp.text().unwrap();
356
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
357
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
358
            assert!(!content.contains("Gruppenverwaltung"));
359
360

            let mut resp = reqwest::get("http://localhost:8080/contest").unwrap();
361
            assert_eq!(resp.status(), StatusCode::OK);
362
363

            let content = resp.text().unwrap();
364
365
            assert!(content.contains("<h1>Wettbewerbe</h1>"));
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
366
367

            let mut resp = reqwest::get("http://localhost:8080/group").unwrap();
368
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
369
            assert!(content.contains("<h1>Login</h1>"));
370
371
        })
    }
Daniel Brüning's avatar
Daniel Brüning committed
372

373
374
    #[test]
    fn check_login_wrong_credentials() {
Robert Czechowski's avatar
Robert Czechowski committed
375
        start_server_and_fn(8081, None, || {
376
            let client = reqwest::Client::new();
Robert Czechowski's avatar
Robert Czechowski committed
377

378
            let mut resp = login(8081, &client, "nonexistingusername", "wrongpassword");
379
            assert_eq!(resp.status(), StatusCode::OK);
380
381

            let content = resp.text().unwrap();
382
383
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Login fehlgeschlagen."));
384
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

            let mut resp = login_code(8081, &client, "g23AgaV");
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Kein gültiger Code."));
            assert!(!content.contains("Error"));

            let mut resp = login_code(8081, &client, "u9XuAbH7p");
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Kein gültiger Code."));
            assert!(!content.contains("Error"));
401
        })
402
    }
403
404

    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
405
    fn check_login() {
Robert Czechowski's avatar
Robert Czechowski committed
406
        start_server_and_fn(8082, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
407
408
409
410
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
411

412
            let mut resp = login(8082, &client, "testusr", "testpw");
413
            assert_eq!(resp.status(), StatusCode::FOUND);
414

415
            let content = resp.text().unwrap();
416
417
            assert!(!content.contains("Error"));

418
419
420
421
            let mut set_cookie = resp.headers().get_all("Set-Cookie").iter();
            assert!(set_cookie.next().is_some());
            assert!(set_cookie.next().is_none());

Robert Czechowski's avatar
Robert Czechowski committed
422
423
424
425
            let location = resp.headers().get(reqwest::header::LOCATION).unwrap().to_str().unwrap();
            assert_eq!(location, "http://localhost:8082/");

            let mut resp = client.get(location).send().unwrap();
426
427
            assert_eq!(resp.status(), StatusCode::OK);

428
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
429
430
431
432
            assert!(!content.contains("Error"));
            assert!(!content.contains("Gruppenverwaltung"));
            assert!(content.contains("Eingeloggt als <em>testusr</em>"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
433
434
435
        })
    }

436
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
437
    fn check_logout() {
Robert Czechowski's avatar
Robert Czechowski committed
438
        start_server_and_fn(8083, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
439
440
441
442
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
443

444
            let resp = login(8083, &client, "testusr", "testpw");
445
446
447
448
449
450
451
            assert_eq!(resp.status(), StatusCode::FOUND);

            let resp = client.get("http://localhost:8083/logout").send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);

            let mut resp = client.get("http://localhost:8083").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);
452
453

            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
454
455
456
457
            assert!(content.contains("Benutzername"));
            assert!(content.contains("Passwort"));
            assert!(content.contains("Gruppencode / Teilnahmecode"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
458
459
460
        })
    }

461
462
    #[test]
    fn check_group_creation_and_group_code_login() {
Robert Czechowski's avatar
Robert Czechowski committed
463
        start_server_and_fn(8084, Some(("testusr".to_string(), "testpw".to_string(), true)), || {
464
465
466
467
468
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

469
            let resp = login(8084, &client, "testusr", "testpw");
470
            assert_eq!(resp.status(), StatusCode::FOUND);
471

472
473
            let mut resp = client.get("http://localhost:8084").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);
474
475

            let content = resp.text().unwrap();
476
477
            assert!(content.contains("[Lehrer]"));
            assert!(content.contains("Gruppenverwaltung"));
478
479
480

            let mut resp = client.get("http://localhost:8084/group/").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);
481
482

            let content = resp.text().unwrap();
483
484
            assert!(content.contains("Gruppe anlegen"));

485
            let params = [("name", "WrongGroupname"), ("tag", "WrongMarker"), ("csrf_token", "76CfTPJaoz")];
486
487
            let resp = client.post("http://localhost:8084/group/").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FORBIDDEN);
488

489
490
491
            let pos = content.find("type=\"hidden\" name=\"csrf_token\" value=\"").expect("CSRF-Token not found");
            let csrf = &content[pos + 39..pos + 49];
            let params = [("name", "Groupname"), ("tag", "Marker"), ("csrf_token", csrf)];
492
493
            let resp = client.post("http://localhost:8084/group/").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
494
495
496
497

            let mut resp = client.get("http://localhost:8084/group/").send().unwrap();
            let content = resp.text().unwrap();
            assert!(!content.contains("WrongGroupname"));
Robert Czechowski's avatar
Robert Czechowski committed
498

499
500
501
502
503
            let pos = content.find("<td><a href=\"/group/1\">Groupname</a></td>").expect("Group not found");
            let groupcode = &content[pos + 58..pos + 65];

            // New client to test group code login
            let client = reqwest::Client::builder().cookie_store(true)
Robert Czechowski's avatar
Robert Czechowski committed
504
505
506
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
507
508
509
510

            let resp = login_code(8084, &client, groupcode);
            assert_eq!(resp.status(), StatusCode::FOUND);

Robert Czechowski's avatar
Robert Czechowski committed
511
512
513
514
515
516
517
518
            let mut set_cookie = resp.headers().get_all("Set-Cookie").iter();
            assert!(set_cookie.next().is_some());
            assert!(set_cookie.next().is_none());

            let location = resp.headers().get(reqwest::header::LOCATION).unwrap().to_str().unwrap();
            assert_eq!(location, "http://localhost:8084/profile");

            let mut resp = client.get(location).send().unwrap();
519
520
521
522
523
524
525
            let content = resp.text().unwrap();

            let pos = content.find("<p>Login-Code: ").expect("Logincode not found");
            let logincode = &content[pos + 15..pos + 24];

            // New client to test login code login
            let client = reqwest::Client::builder().cookie_store(true)
Robert Czechowski's avatar
Robert Czechowski committed
526
527
528
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
529
530
531
532

            let resp = login_code(8084, &client, logincode);
            assert_eq!(resp.status(), StatusCode::FOUND);

Robert Czechowski's avatar
Robert Czechowski committed
533
534
535
536
            let location = resp.headers().get(reqwest::header::LOCATION).unwrap().to_str().unwrap();
            assert_eq!(location, "http://localhost:8084/");

            let mut resp = client.get(location).send().unwrap();
537
538
            let content = resp.text().unwrap();
            assert!(content.contains("Eingeloggt als <em></em>"));
539
540
        })
    }
Robert Czechowski's avatar
Robert Czechowski committed
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573

    #[test]
    fn check_contest_start() {
        start_server_and_fn(8085, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

            let resp = login(8085, &client, "testusr", "testpw");
            assert_eq!(resp.status(), StatusCode::FOUND);

            let mut resp = client.get("http://localhost:8085/contest/").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert!(content.contains("PublicContestName"));
            assert!(content.contains("InfiniteContestName"));
            //assert!(content.contains("PrivateContestName"));
            assert!(!content.contains("WrongContestName"));
            assert!(!content.contains("RenamedContestName"));
            assert!(content.contains("<a href=\"/contest/1\">PublicContestName</a>"));

            let mut resp = client.get("http://localhost:8085/contest/1").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert!(content.contains("PublicContestName"));
            assert!(!content.contains("InfiniteContestName"));
            assert!(!content.contains("PrivateContestName"));
            assert!(!content.contains("WrongContestName"));
            assert!(!content.contains("RenamedContestName"));

574
            let params = [("csrf_token", "76CfTPJaoz")];
Robert Czechowski's avatar
Robert Czechowski committed
575
576
577
            let resp = client.post("http://localhost:8085/contest/1").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FORBIDDEN);

578
579
580
            let pos = content.find("type=\"hidden\" name=\"csrf_token\" value=\"").expect("CSRF-Token not found");
            let csrf = &content[pos + 39..pos + 49];
            let params = [("csrf_token", csrf)];
Robert Czechowski's avatar
Robert Czechowski committed
581
582
583
584
            let resp = client.post("http://localhost:8085/contest/1").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
        })
    }
585
}