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

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

Robert Czechowski's avatar
Robert Czechowski committed
10
extern crate handlebars_iron;
Robert Czechowski's avatar
Robert Czechowski committed
11
12
extern crate iron_sessionstorage;
extern crate mount;
13
extern crate params;
Robert Czechowski's avatar
Robert Czechowski committed
14
15
extern crate persistent;
extern crate rand;
16
extern crate reqwest;
Robert Czechowski's avatar
Robert Czechowski committed
17
extern crate serde_json;
18
extern crate serde_yaml;
Robert Czechowski's avatar
Robert Czechowski committed
19
20
21
22
extern crate staticfile;
extern crate structopt;
extern crate time;
extern crate urlencoded;
23
24
25

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

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

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

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

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

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

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

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

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

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

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

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

    contests
}

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

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

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

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

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

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

146
147
148
149
150
151
152
fn prepare_and_start_server<C>(mut conn: C, config: Config, onlycontestscan: bool, resetadminpw: bool)
    where C: MedalConnection + std::marker::Send + 'static,
          db_objects::Contest: db_conn::MedalObject<C>
{
    db_apply_migrations::test(&mut conn);

    if onlycontestscan || config.no_contest_scan == Some(false) {
153
        print!("Scanning for contests …");
154
        refresh_all_contests(&mut conn);
155
        println!(" Done")
156
157
158
159
160
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            prepare_and_start_server(conn, config, opt.onlycontestscan, opt.resetadminpw);
            return;
        }
226
    }
227
228
229
230
231
232
233
234
235
236
237
238
239
240

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

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

    println!("No database configured. Try enableing the 'rusqlite' feature during compilation.\nLeaving now.");
241
}
242
243
244
245

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

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

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

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

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

279
            // ID: 1
Robert Czechowski's avatar
Robert Czechowski committed
280
281
282
283
284
285
            let mut contest = Contest::new("directory".to_string(),
                                           "public.yaml".to_string(),
                                           "PublicContestName".to_string(),
                                           1,
                                           true,
                                           None,
286
287
                                           None,
                                           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,
Robert Czechowski's avatar
Robert Czechowski committed
306
                                           None);
307
            let mut taskgroup = Taskgroup::new("TaskgroupName".to_string(), None);
308
            let task = Task::new("taskdir1".to_string(), 3); // ID: 3
Robert Czechowski's avatar
Robert Czechowski committed
309
            taskgroup.tasks.push(task);
310
            let task = Task::new("taskdir2".to_string(), 4); // ID: 4
Robert Czechowski's avatar
Robert Czechowski committed
311
312
313
314
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

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

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

346
            // Message server started
347
348
            start_tx.send(()).unwrap();

349
            // Wait for test to finish
350
351
            stop_rx.recv().unwrap();

352
            srvr.close().unwrap();
353
354
        });

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

        // Run test code
360
        f();
361

362
        // Message test finished
363
364
365
        stop_tx.send(()).unwrap();
    }

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

372
373
374
375
376
    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
    }
377

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

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

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

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

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

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

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

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

            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"));
430
        })
431
    }
432
433

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            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
527

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

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

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

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

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

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

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

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

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

            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>"));
619
620
621
622
623
        })
    }

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

630
            let resp = client.get("http://localhost:8086/contest/3").send().unwrap();
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
            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");
695
            assert_eq!(resp.status(), StatusCode::FOUND);
Robert Czechowski's avatar
Robert Czechowski committed
696

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

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

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

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

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

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

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

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

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

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

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

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

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

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