main.rs 31 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
15
extern crate persistent;
extern crate rand;
16
extern crate reqwest;
Robert Czechowski's avatar
Robert Czechowski committed
17
extern crate serde_json;
18
extern crate serde_yaml;
Robert Czechowski's avatar
Robert Czechowski committed
19
20
21
22
extern crate staticfile;
extern crate structopt;
extern crate time;
extern crate urlencoded;
23
24
25

#[cfg(feature = "postgres")]
extern crate postgres;
26
27
28
#[cfg(feature = "rusqlite_old")]
extern crate rusqlite;
#[cfg(feature = "rusqlite_new")]
29
extern crate rusqlite;
30
#[cfg(feature = "webbrowser")]
31
extern crate webbrowser;
32

33
34
pub mod config;
pub mod contestreader_yaml;
35
pub mod core;
36
pub mod db_conn;
37
pub mod helpers;
38
39
40
pub mod oauth_provider;

mod db_apply_migrations;
41
mod db_conn_postgres;
42
mod db_conn_sqlite;
43
mod db_conn_sqlite_new;
44
mod db_objects;
45
46
mod webfw_iron;

47
use db_conn::{MedalConnection, MedalObject};
48
use db_objects::*;
49
use helpers::SetPassword;
Robert Czechowski's avatar
Robert Czechowski committed
50
51
use webfw_iron::start_server;

52
use config::Config;
53
54
use structopt::StructOpt;

55
use std::path::{Path, PathBuf};
56

57
fn read_contest(p: &PathBuf) -> Option<Contest> {
58
59
    use std::fs::File;
    use std::io::Read;
60

61
62
63
    let mut file = File::open(p).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
64

65
    contestreader_yaml::parse_yaml(&contents,
66
67
                                   p.file_name().to_owned()?.to_str()?,
                                   &format!("{}/", p.parent().unwrap().to_str()?))
Robert Czechowski's avatar
Robert Czechowski committed
68
69
70
}

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

79
        if p.file_name().unwrap().to_string_lossy().to_string().ends_with(".yaml") {
80
            read_contest(p).map(|contest| contests.push(contest));
81
        };
Robert Czechowski's avatar
Robert Czechowski committed
82
83
84
    };

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

    contests
}

97
98
99
100
fn refresh_all_contests<C>(conn: &mut C)
    where C: MedalConnection,
          db_objects::Contest: db_conn::MedalObject<C>
{
101
    conn.reset_all_contest_visibilities();
Robert Czechowski's avatar
Robert Czechowski committed
102
103
104
105
106
107
108
    let v = get_all_contest_info("tasks/");

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

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

Robert Czechowski's avatar
Robert Czechowski committed
125
    use rand::{distributions::Alphanumeric, thread_rng, Rng};
126
127

    let password: String = thread_rng().sample_iter(&Alphanumeric)
Robert Czechowski's avatar
Robert Czechowski committed
128
129
130
131
132
133
                                       .filter(|x| {
                                           let x = *x;
                                           !(x == 'l' || x == 'I' || x == '1' || x == 'O' || x == 'o' || x == '0')
                                       })
                                       .take(8)
                                       .collect();
134
135
136
137
    print!("'{}' …", &password);

    admin.username = Some("admin".into());
    match admin.set_password(&password) {
138
        None => println!(" FAILED! (Password hashing error)"),
139
140
        _ => {
            conn.save_session(admin);
141
            println!(" Done");
142
        }
143
144
145
    }
}

146
147
148
149
150
151
152
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) {
153
        print!("Scanning for contests …");
154
        refresh_all_contests(&mut conn);
155
        println!(" Done")
156
157
158
159
160
    }

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

161
        #[cfg(feature = "webbrowser")]
162
        let self_url = config.self_url.clone();
163
        #[cfg(feature = "webbrowser")]
164
165
        let open_browser = config.open_browser;

166
        match start_server(conn, config) {
167
168
            Ok(_) => {
                println!("Server started");
169

170
171
172
173
174
                #[cfg(feature = "webbrowser")]
                {
                    if let (Some(self_url), Some(true)) = (self_url, open_browser) {
                        open_browser_window(&self_url);
                    }
175
                }
176
            }
177
178
            Err(_) => println!("Error on server start …"),
        };
179

180
181
182
183
        println!("Could not run server. Is the port already in use?");
    }
}

184
#[cfg(feature = "webbrowser")]
185
186
187
fn open_browser_window(self_url: &str) {
    match webbrowser::open(&self_url) {
        Ok(_) => (),
188
        Err(e) => println!("Error while opening webbrowser: {:?}", e),
189
190
191
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
192
fn main() {
193
    let opt = config::Opt::from_args();
194
195
196

    #[cfg(feature = "debug")]
    println!("Options: {:#?}", opt);
Daniel Brüning's avatar
Daniel Brüning committed
197

198
    let mut config = config::read_config_from_file(&opt.configfile);
199

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    #[cfg(feature = "debug")]
    println!("Config: {:#?}", config);

    // Let options override config values
    opt.databasefile.map(|x| config.database_file = Some(x));
    opt.databaseurl.map(|x| config.database_url = Some(x));
    opt.port.map(|x| config.port = Some(x));
    config.no_contest_scan = if opt.nocontestscan { Some(true) } else { config.no_contest_scan };
    config.open_browser = if opt.openbrowser { Some(true) } else { config.open_browser };

    // Use default database file if none set
    config.database_file.get_or_insert(Path::new("medal.db").to_owned());

    #[cfg(feature = "debug")]
    println!("Using config: {:#?}", config);
215

216
217
218
219
220
221
222
223
224
225
    #[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;
        }
226
    }
227
228
229
230
231
232
233
234
235
236
237
238
239
240

    #[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.");
241
}
242
243
244
245

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

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

        thread::spawn(move || {
256
            let mut conn = rusqlite::Connection::open_in_memory().unwrap();
257
258
            db_apply_migrations::test(&mut conn);

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

267
            // ID: 1, gets renamed
Robert Czechowski's avatar
Robert Czechowski committed
268
269
270
271
272
273
274
275
276
            let mut contest = Contest::new("directory".to_string(),
                                           "public.yaml".to_string(),
                                           "RenamedContestName".to_string(),
                                           1,
                                           true,
                                           None,
                                           None);
            contest.save(&conn);

277
            // ID: 1
Robert Czechowski's avatar
Robert Czechowski committed
278
279
280
281
282
283
284
            let mut contest = Contest::new("directory".to_string(),
                                           "public.yaml".to_string(),
                                           "PublicContestName".to_string(),
                                           1,
                                           true,
                                           None,
                                           None);
285
            let mut taskgroup = Taskgroup::new("TaskgroupName".to_string(), None);
286
            let task = Task::new("taskdir1".to_string(), 3); // ID: 1
Robert Czechowski's avatar
Robert Czechowski committed
287
            taskgroup.tasks.push(task);
288
            let task = Task::new("taskdir2".to_string(), 4); // ID: 2
Robert Czechowski's avatar
Robert Czechowski committed
289
290
291
292
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

293
            // ID: 2
Robert Czechowski's avatar
Robert Czechowski committed
294
295
296
297
298
299
300
            let mut contest = Contest::new("directory".to_string(),
                                           "private.yaml".to_string(),
                                           "PrivateContestName".to_string(),
                                           1,
                                           false,
                                           None,
                                           None);
301
            let mut taskgroup = Taskgroup::new("TaskgroupName".to_string(), None);
302
            let task = Task::new("taskdir1".to_string(), 3); // ID: 3
Robert Czechowski's avatar
Robert Czechowski committed
303
            taskgroup.tasks.push(task);
304
            let task = Task::new("taskdir2".to_string(), 4); // ID: 4
Robert Czechowski's avatar
Robert Czechowski committed
305
306
307
308
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

309
            // ID: 3
Robert Czechowski's avatar
Robert Czechowski committed
310
311
312
313
314
315
316
            let mut contest = Contest::new("directory".to_string(),
                                           "infinte.yaml".to_string(),
                                           "InfiniteContestName".to_string(),
                                           0,
                                           true,
                                           None,
                                           None);
317
318
319
320
321
322
323
324
325
            let mut taskgroup = Taskgroup::new("TaskgroupRenameName".to_string(), None);
            let task = Task::new("taskdir1".to_string(), 3); // ID: 5
            taskgroup.tasks.push(task);
            let task = Task::new("taskdir2".to_string(), 4); // ID: 6
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

            let mut taskgroup = Taskgroup::new("TaskgroupNewName".to_string(), None);
326
            let task = Task::new("taskdir1".to_string(), 3); // ID: 5
Robert Czechowski's avatar
Robert Czechowski committed
327
            taskgroup.tasks.push(task);
328
            let task = Task::new("taskdir2".to_string(), 4); // ID: 6
Robert Czechowski's avatar
Robert Czechowski committed
329
330
331
332
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

333
            let mut config = config::read_config_from_file(Path::new("thisfileshoudnotexist"));
334
            config.port = Some(port);
335
            config.cookie_signing_secret = Some("testtesttesttesttesttesttesttest".to_string());
336
            let mut srvr = start_server(conn, config).expect(&format!("Could not start server on port {}", port));
337

338
            // Message server started
339
340
            start_tx.send(()).unwrap();

341
            // Wait for test to finish
342
343
            stop_rx.recv().unwrap();

344
            srvr.close().unwrap();
345
346
        });

347
        // Wait for server to start
348
349
        start_rx.recv().unwrap();
        thread::sleep(time::Duration::from_millis(100));
350
351

        // Run test code
352
        f();
353

354
        // Message test finished
355
356
357
        stop_tx.send(()).unwrap();
    }

358
    fn login(port: u16, client: &reqwest::Client, username: &str, password: &str) -> reqwest::Response {
359
        let params = [("username", username), ("password", password)];
Robert Czechowski's avatar
Robert Czechowski committed
360
        let resp = client.post(&format!("http://localhost:{}/login", port)).form(&params).send().unwrap();
361
        resp
362
    }
Robert Czechowski's avatar
Robert Czechowski committed
363

364
365
366
367
368
    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
    }
369

370
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
371
    fn start_server_and_check_requests() {
Robert Czechowski's avatar
Robert Czechowski committed
372
        start_server_and_fn(8080, None, || {
373
            let mut resp = reqwest::get("http://localhost:8080").unwrap();
374
            assert_eq!(resp.status(), StatusCode::OK);
375
376

            let content = resp.text().unwrap();
377
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
378
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
379
            assert!(!content.contains("Gruppenverwaltung"));
380
381

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

            let content = resp.text().unwrap();
385
386
            assert!(content.contains("<h1>Wettbewerbe</h1>"));
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
387
388

            let mut resp = reqwest::get("http://localhost:8080/group").unwrap();
389
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
390
            assert!(content.contains("<h1>Login</h1>"));
391
392
        })
    }
Daniel Brüning's avatar
Daniel Brüning committed
393

394
395
    #[test]
    fn check_login_wrong_credentials() {
Robert Czechowski's avatar
Robert Czechowski committed
396
        start_server_and_fn(8081, None, || {
397
            let client = reqwest::Client::new();
Robert Czechowski's avatar
Robert Czechowski committed
398

399
            let mut resp = login(8081, &client, "nonexistingusername", "wrongpassword");
400
            assert_eq!(resp.status(), StatusCode::OK);
401
402

            let content = resp.text().unwrap();
403
404
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Login fehlgeschlagen."));
405
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421

            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"));
422
        })
423
    }
424
425

    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
426
    fn check_login() {
Robert Czechowski's avatar
Robert Czechowski committed
427
        start_server_and_fn(8082, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
428
429
430
431
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
432

433
            let mut resp = login(8082, &client, "testusr", "testpw");
434
            assert_eq!(resp.status(), StatusCode::FOUND);
435

436
            let content = resp.text().unwrap();
437
438
            assert!(!content.contains("Error"));

439
440
441
442
            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
443
444
445
446
            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();
447
448
            assert_eq!(resp.status(), StatusCode::OK);

449
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
450
451
452
453
            assert!(!content.contains("Error"));
            assert!(!content.contains("Gruppenverwaltung"));
            assert!(content.contains("Eingeloggt als <em>testusr</em>"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
454
455
456
        })
    }

457
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
458
    fn check_logout() {
Robert Czechowski's avatar
Robert Czechowski committed
459
        start_server_and_fn(8083, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
460
461
462
463
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
464

465
            let resp = login(8083, &client, "testusr", "testpw");
466
467
468
469
470
471
472
            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);
473
474

            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
475
476
477
478
            assert!(content.contains("Benutzername"));
            assert!(content.contains("Passwort"));
            assert!(content.contains("Gruppencode / Teilnahmecode"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
479
480
481
        })
    }

482
483
    #[test]
    fn check_group_creation_and_group_code_login() {
Robert Czechowski's avatar
Robert Czechowski committed
484
        start_server_and_fn(8084, Some(("testusr".to_string(), "testpw".to_string(), true)), || {
485
486
487
488
489
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

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

493
494
            let mut resp = client.get("http://localhost:8084").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);
495
496

            let content = resp.text().unwrap();
497
498
            assert!(content.contains("[Lehrer]"));
            assert!(content.contains("Gruppenverwaltung"));
499
500
501

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

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

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

510
511
512
            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)];
513
514
            let resp = client.post("http://localhost:8084/group/").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
515
516
517
518

            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
519

520
521
522
523
524
            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
525
526
527
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
528
529
530
531

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

Robert Czechowski's avatar
Robert Czechowski committed
532
533
534
535
536
537
538
539
            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();
540
541
542
543
544
545
546
            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
547
548
549
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
550
551
552
553

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

Robert Czechowski's avatar
Robert Czechowski committed
554
555
556
557
            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();
558
559
            let content = resp.text().unwrap();
            assert!(content.contains("Eingeloggt als <em></em>"));
560
561
        })
    }
Robert Czechowski's avatar
Robert Czechowski committed
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594

    #[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"));

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

599
600
601
            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
602
603
            let resp = client.post("http://localhost:8085/contest/1").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
Robert Czechowski's avatar
Robert Czechowski committed
604
605
606
607
608
609
610

            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("<a href=\"/task/1\">☆☆☆</a></li>"));
            assert!(content.contains("<a href=\"/task/2\">☆☆☆☆</a></li>"));
611
612
613
614
615
        })
    }

    #[test]
    fn check_task_load_save() {
616
        start_server_and_fn(8086, None, || {
617
618
619
620
621
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

622
            let resp = client.get("http://localhost:8086/contest/3").send().unwrap();
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
            assert_eq!(resp.status(), StatusCode::OK);

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

            let content = resp.text().unwrap();
            let pos = content.find("#taskid=5&csrftoken=").expect("CSRF-Token not found");
            let csrf = &content[pos + 20..pos + 30];

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

            let content = resp.text().unwrap();
            assert_eq!(content, "{}");

            let params = [("data", "WrongData"), ("grade", "1"), ("csrf_token", "FNQU4QsEMY")];
            let resp = client.post("http://localhost:8086/save/5").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FORBIDDEN);

            // Check that the illegitimate request did not actually change anything
            let mut resp = client.get("http://localhost:8086/load/5").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert_eq!(content, "{}");

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

            let content = resp.text().unwrap();
            assert!(content.contains("<a href=\"/task/5\">☆☆☆</a></li>"));
            assert!(content.contains("<a href=\"/task/6\">☆☆☆☆</a></li>"));

            let params = [("data", "SomeData"), ("grade", "2"), ("csrf_token", csrf)];
            let mut resp = client.post("http://localhost:8086/save/5").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert_eq!(content, "{}");

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

            let content = resp.text().unwrap();
            assert_eq!(content, "SomeData");

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

            let content = resp.text().unwrap();
            assert!(content.contains("<a href=\"/task/5\">★★☆</a></li>"));
            assert!(content.contains("<a href=\"/task/6\">☆☆☆☆</a></li>"));
        })
    }

    #[test]
    fn check_task_load_save_logged_in() {
        start_server_and_fn(8087, 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(8087, &client, "testusr", "testpw");
687
            assert_eq!(resp.status(), StatusCode::FOUND);
Robert Czechowski's avatar
Robert Czechowski committed
688

689
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
690
691
692
693
694
695
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            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)];
696
            let resp = client.post("http://localhost:8087/contest/1").form(&params).send().unwrap();
697
698
            assert_eq!(resp.status(), StatusCode::FOUND);

699
            let mut resp = client.get("http://localhost:8087/task/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
700
701
702
703
704
705
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            let pos = content.find("#taskid=1&csrftoken=").expect("CSRF-Token not found");
            let csrf = &content[pos + 20..pos + 30];

706
            let mut resp = client.get("http://localhost:8087/load/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
707
            assert_eq!(resp.status(), StatusCode::OK);
708

Robert Czechowski's avatar
Robert Czechowski committed
709
710
711
            let content = resp.text().unwrap();
            assert_eq!(content, "{}");

712
            let params = [("data", "WrongData"), ("grade", "1"), ("csrf_token", "FNQU4QsEMY")];
713
            let resp = client.post("http://localhost:8087/save/1").form(&params).send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
714
715
            assert_eq!(resp.status(), StatusCode::FORBIDDEN);

716
            // Check that the illigal request did not actually change anything
717
            let mut resp = client.get("http://localhost:8087/load/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
718
            assert_eq!(resp.status(), StatusCode::OK);
719

Robert Czechowski's avatar
Robert Czechowski committed
720
721
            let content = resp.text().unwrap();
            assert_eq!(content, "{}");
722

723
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
724
725
726
727
728
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert!(content.contains("<a href=\"/task/1\">☆☆☆</a></li>"));
            assert!(content.contains("<a href=\"/task/2\">☆☆☆☆</a></li>"));
729
730

            let params = [("data", "SomeData"), ("grade", "2"), ("csrf_token", csrf)];
731
            let mut resp = client.post("http://localhost:8087/save/1").form(&params).send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
732
733
734
735
736
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert_eq!(content, "{}");

737
            let mut resp = client.get("http://localhost:8087/load/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
738
            assert_eq!(resp.status(), StatusCode::OK);
739

Robert Czechowski's avatar
Robert Czechowski committed
740
741
            let content = resp.text().unwrap();
            assert_eq!(content, "SomeData");
742

743
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
744
745
746
747
748
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert!(content.contains("<a href=\"/task/1\">★★☆</a></li>"));
            assert!(content.contains("<a href=\"/task/2\">☆☆☆☆</a></li>"));
Robert Czechowski's avatar
Robert Czechowski committed
749
750
        })
    }
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775

    #[test]
    fn check_taskgroup_rename() {
        start_server_and_fn(8088, None, || {
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

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

            let content = resp.text().unwrap();
            println!("{}", content);
            assert!(content.contains("TaskgroupNewName"));
            assert!(!content.contains("TaskgroupRenameName"));

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

            let content = resp.text().unwrap();
            assert!(content.contains("TaskgroupNewName"));
            assert!(!content.contains("TaskgroupRenameName"));
        })
    }
776
}