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
#[cfg(feature = "rusqlite")]
27
extern crate rusqlite;
28
#[cfg(feature = "webbrowser")]
29
extern crate webbrowser;
30

31
32
pub mod config;
pub mod contestreader_yaml;
33
pub mod core;
34
pub mod db_conn;
35
pub mod helpers;
36
37
38
pub mod oauth_provider;

mod db_apply_migrations;
39
mod db_conn_postgres;
40
mod db_conn_sqlite_new;
41
mod db_objects;
42
43
mod webfw_iron;

44
use db_conn::{MedalConnection, MedalObject};
45
use db_objects::*;
46
use helpers::SetPassword;
Robert Czechowski's avatar
Robert Czechowski committed
47
48
use webfw_iron::start_server;

49
use config::Config;
50
51
use structopt::StructOpt;

52
use std::path::{Path, PathBuf};
53

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

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

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

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

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

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

    contests
}

94
95
96
97
fn refresh_all_contests<C>(conn: &mut C)
    where C: MedalConnection,
          db_objects::Contest: db_conn::MedalObject<C>
{
98
    conn.reset_all_contest_visibilities();
99
100
    conn.reset_all_taskgroup_visibilities();

Robert Czechowski's avatar
Robert Czechowski committed
101
102
103
104
105
106
107
    let v = get_all_contest_info("tasks/");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

199
200
201
202
203
204
205
206
207
    #[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 };
208
    config.disable_results_page = if opt.disableresultspage { Some(true) } else { config.disable_results_page };
209
210
211
212
213
214

    // 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
            let mut contest = Contest::new("directory".to_string(),
                                           "public.yaml".to_string(),
                                           "RenamedContestName".to_string(),
                                           1,
                                           true,
                                           None,
274
275
                                           None,
                                           None,
276
                                           None,
Robert Czechowski's avatar
Robert Czechowski committed
277
278
279
                                           None);
            contest.save(&conn);

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

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

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

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

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

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

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

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

        // Run test code
364
        f();
365

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            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
531

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

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

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

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

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

Robert Czechowski's avatar
Robert Czechowski committed
566
567
568
569
            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();
570
571
            let content = resp.text().unwrap();
            assert!(content.contains("Eingeloggt als <em></em>"));
572
573
        })
    }
Robert Czechowski's avatar
Robert Czechowski committed
574
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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

755
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
756
757
758
759
760
            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
761
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

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