main.rs 31.7 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
    #[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 };
209
    config.disable_results_page = if opt.disableresultspage { Some(true) } else { config.disable_results_page };
210
211
212
213
214
215

    // 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);
216

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

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

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

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

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

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

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

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

300
            // ID: 2
Robert Czechowski's avatar
Robert Czechowski committed
301
302
303
304
305
306
            let mut contest = Contest::new("directory".to_string(),
                                           "private.yaml".to_string(),
                                           "PrivateContestName".to_string(),
                                           1,
                                           false,
                                           None,
307
308
                                           None,
                                           None,
309
                                           None,
Robert Czechowski's avatar
Robert Czechowski committed
310
                                           None);
311
            let mut taskgroup = Taskgroup::new("TaskgroupName".to_string(), None);
312
            let task = Task::new("taskdir1".to_string(), 3); // ID: 3
Robert Czechowski's avatar
Robert Czechowski committed
313
            taskgroup.tasks.push(task);
314
            let task = Task::new("taskdir2".to_string(), 4); // ID: 4
Robert Czechowski's avatar
Robert Czechowski committed
315
316
317
318
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

319
            // ID: 3
Robert Czechowski's avatar
Robert Czechowski committed
320
321
322
323
324
325
            let mut contest = Contest::new("directory".to_string(),
                                           "infinte.yaml".to_string(),
                                           "InfiniteContestName".to_string(),
                                           0,
                                           true,
                                           None,
326
327
                                           None,
                                           None,
328
                                           None,
Robert Czechowski's avatar
Robert Czechowski committed
329
                                           None);
330
331
332
333
334
335
336
337
338
            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);
339
            let task = Task::new("taskdir1".to_string(), 3); // ID: 5
Robert Czechowski's avatar
Robert Czechowski committed
340
            taskgroup.tasks.push(task);
341
            let task = Task::new("taskdir2".to_string(), 4); // ID: 6
Robert Czechowski's avatar
Robert Czechowski committed
342
343
344
345
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

346
            let mut config = config::read_config_from_file(Path::new("thisfileshoudnotexist"));
347
            config.port = Some(port);
348
            config.cookie_signing_secret = Some("testtesttesttesttesttesttesttest".to_string());
349
            let mut srvr = start_server(conn, config).expect(&format!("Could not start server on port {}", port));
350

351
            // Message server started
352
353
            start_tx.send(()).unwrap();

354
            // Wait for test to finish
355
356
            stop_rx.recv().unwrap();

357
            srvr.close().unwrap();
358
359
        });

360
        // Wait for server to start
361
362
        start_rx.recv().unwrap();
        thread::sleep(time::Duration::from_millis(100));
363
364

        // Run test code
365
        f();
366

367
        // Message test finished
368
369
370
        stop_tx.send(()).unwrap();
    }

371
    fn login(port: u16, client: &reqwest::Client, username: &str, password: &str) -> reqwest::Response {
372
        let params = [("username", username), ("password", password)];
Robert Czechowski's avatar
Robert Czechowski committed
373
        let resp = client.post(&format!("http://localhost:{}/login", port)).form(&params).send().unwrap();
374
        resp
375
    }
Robert Czechowski's avatar
Robert Czechowski committed
376

377
378
379
380
381
    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
    }
382

383
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
384
    fn start_server_and_check_requests() {
Robert Czechowski's avatar
Robert Czechowski committed
385
        start_server_and_fn(8080, None, || {
386
            let mut resp = reqwest::get("http://localhost:8080").unwrap();
387
            assert_eq!(resp.status(), StatusCode::OK);
388
389

            let content = resp.text().unwrap();
390
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
391
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
392
            assert!(!content.contains("Gruppenverwaltung"));
393
394

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

            let content = resp.text().unwrap();
398
399
            assert!(content.contains("<h1>Wettbewerbe</h1>"));
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
400
401

            let mut resp = reqwest::get("http://localhost:8080/group").unwrap();
402
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
403
            assert!(content.contains("<h1>Login</h1>"));
404
405
        })
    }
Daniel Brüning's avatar
Daniel Brüning committed
406

407
408
    #[test]
    fn check_login_wrong_credentials() {
Robert Czechowski's avatar
Robert Czechowski committed
409
        start_server_and_fn(8081, None, || {
410
            let client = reqwest::Client::new();
Robert Czechowski's avatar
Robert Czechowski committed
411

412
            let mut resp = login(8081, &client, "nonexistingusername", "wrongpassword");
413
            assert_eq!(resp.status(), StatusCode::OK);
414
415

            let content = resp.text().unwrap();
416
417
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Login fehlgeschlagen."));
418
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434

            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"));
435
        })
436
    }
437
438

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

446
            let mut resp = login(8082, &client, "testusr", "testpw");
447
            assert_eq!(resp.status(), StatusCode::FOUND);
448

449
            let content = resp.text().unwrap();
450
451
            assert!(!content.contains("Error"));

452
453
454
455
            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
456
457
458
459
            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();
460
461
            assert_eq!(resp.status(), StatusCode::OK);

462
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
463
464
465
466
            assert!(!content.contains("Error"));
            assert!(!content.contains("Gruppenverwaltung"));
            assert!(content.contains("Eingeloggt als <em>testusr</em>"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
467
468
469
        })
    }

470
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
471
    fn check_logout() {
Robert Czechowski's avatar
Robert Czechowski committed
472
        start_server_and_fn(8083, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
473
474
475
476
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
477

478
            let resp = login(8083, &client, "testusr", "testpw");
479
480
481
482
483
484
485
            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);
486
487

            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
488
489
490
491
            assert!(content.contains("Benutzername"));
            assert!(content.contains("Passwort"));
            assert!(content.contains("Gruppencode / Teilnahmecode"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
492
493
494
        })
    }

495
496
    #[test]
    fn check_group_creation_and_group_code_login() {
Robert Czechowski's avatar
Robert Czechowski committed
497
        start_server_and_fn(8084, Some(("testusr".to_string(), "testpw".to_string(), true)), || {
498
499
500
501
502
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

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

506
507
            let mut resp = client.get("http://localhost:8084").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);
508
509

            let content = resp.text().unwrap();
510
511
            assert!(content.contains("[Lehrer]"));
            assert!(content.contains("Gruppenverwaltung"));
512
513
514

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

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

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

523
524
525
            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)];
526
527
            let resp = client.post("http://localhost:8084/group/").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
528
529
530
531

            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
532

533
534
535
536
537
            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
538
539
540
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
541
542
543
544

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

Robert Czechowski's avatar
Robert Czechowski committed
545
546
547
548
549
            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();
550
            assert_eq!(location, "http://localhost:8084/profile?status=firstlogin");
Robert Czechowski's avatar
Robert Czechowski committed
551
552

            let mut resp = client.get(location).send().unwrap();
553
554
555
556
557
558
559
            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
560
561
562
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
563
564
565
566

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

Robert Czechowski's avatar
Robert Czechowski committed
567
568
569
570
            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();
571
572
            let content = resp.text().unwrap();
            assert!(content.contains("Eingeloggt als <em></em>"));
573
574
        })
    }
Robert Czechowski's avatar
Robert Czechowski committed
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607

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

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

612
613
614
            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
615
616
            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
617
618
619
620
621
622
623

            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>"));
624
625
626
627
628
        })
    }

    #[test]
    fn check_task_load_save() {
629
        start_server_and_fn(8086, None, || {
630
631
632
633
634
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

635
            let resp = client.get("http://localhost:8086/contest/3").send().unwrap();
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
687
688
689
690
691
692
693
694
695
696
697
698
699
            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");
700
            assert_eq!(resp.status(), StatusCode::FOUND);
Robert Czechowski's avatar
Robert Czechowski committed
701

702
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
703
704
705
706
707
708
            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)];
709
            let resp = client.post("http://localhost:8087/contest/1").form(&params).send().unwrap();
710
711
            assert_eq!(resp.status(), StatusCode::FOUND);

712
            let mut resp = client.get("http://localhost:8087/task/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
713
714
715
716
717
718
            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];

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

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

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

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

Robert Czechowski's avatar
Robert Czechowski committed
733
734
            let content = resp.text().unwrap();
            assert_eq!(content, "{}");
735

736
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
737
738
739
740
741
            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>"));
742
743

            let params = [("data", "SomeData"), ("grade", "2"), ("csrf_token", csrf)];
744
            let mut resp = client.post("http://localhost:8087/save/1").form(&params).send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
745
746
747
748
749
            assert_eq!(resp.status(), StatusCode::OK);

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

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

Robert Czechowski's avatar
Robert Czechowski committed
753
754
            let content = resp.text().unwrap();
            assert_eq!(content, "SomeData");
755

756
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
757
758
759
760
761
            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
762
763
        })
    }
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788

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