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();
Robert Czechowski's avatar
Robert Czechowski committed
99
100
101
102
103
104
105
    let v = get_all_contest_info("tasks/");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

195
    let mut config = config::read_config_from_file(&opt.configfile);
196

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

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

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

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

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

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

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

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

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

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

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

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

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

348
            // Message server started
349
350
            start_tx.send(()).unwrap();

351
            // Wait for test to finish
352
353
            stop_rx.recv().unwrap();

354
            srvr.close().unwrap();
355
356
        });

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

        // Run test code
362
        f();
363

364
        // Message test finished
365
366
367
        stop_tx.send(()).unwrap();
    }

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

374
375
376
377
378
    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
    }
379

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

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

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

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

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

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

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

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

            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"));
432
        })
433
    }
434
435

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            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
529

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

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

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

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

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

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

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

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

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

            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>"));
621
622
623
624
625
        })
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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