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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    contests
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

325
            let mut config = config::read_config_from_file(Path::new("thisfileshoudnotexist"));
326
            config.port = Some(port);
327
            config.cookie_signing_secret = Some("testtesttesttesttesttesttesttest".to_string());
328
            let mut srvr = start_server(conn, config).expect(&format!("Could not start server on port {}", port));
329

330
            // Message server started
331
332
            start_tx.send(()).unwrap();

333
            // Wait for test to finish
334
335
            stop_rx.recv().unwrap();

336
            srvr.close().unwrap();
337
338
        });

339
        // Wait for server to start
340
341
        start_rx.recv().unwrap();
        thread::sleep(time::Duration::from_millis(100));
342
343

        // Run test code
344
        f();
345

346
        // Message test finished
347
348
349
        stop_tx.send(()).unwrap();
    }

350
    fn login(port: u16, client: &reqwest::Client, username: &str, password: &str) -> reqwest::Response {
351
        let params = [("username", username), ("password", password)];
Robert Czechowski's avatar
Robert Czechowski committed
352
        let resp = client.post(&format!("http://localhost:{}/login", port)).form(&params).send().unwrap();
353
        resp
354
    }
Robert Czechowski's avatar
Robert Czechowski committed
355

356
357
358
359
360
    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
    }
361

362
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
363
    fn start_server_and_check_requests() {
Robert Czechowski's avatar
Robert Czechowski committed
364
        start_server_and_fn(8080, None, || {
365
            let mut resp = reqwest::get("http://localhost:8080").unwrap();
366
            assert_eq!(resp.status(), StatusCode::OK);
367
368

            let content = resp.text().unwrap();
369
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
370
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
371
            assert!(!content.contains("Gruppenverwaltung"));
372
373

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

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

            let mut resp = reqwest::get("http://localhost:8080/group").unwrap();
381
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
382
            assert!(content.contains("<h1>Login</h1>"));
383
384
        })
    }
Daniel Brüning's avatar
Daniel Brüning committed
385

386
387
    #[test]
    fn check_login_wrong_credentials() {
Robert Czechowski's avatar
Robert Czechowski committed
388
        start_server_and_fn(8081, None, || {
389
            let client = reqwest::Client::new();
Robert Czechowski's avatar
Robert Czechowski committed
390

391
            let mut resp = login(8081, &client, "nonexistingusername", "wrongpassword");
392
            assert_eq!(resp.status(), StatusCode::OK);
393
394

            let content = resp.text().unwrap();
395
396
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Login fehlgeschlagen."));
397
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413

            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"));
414
        })
415
    }
416
417

    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
418
    fn check_login() {
Robert Czechowski's avatar
Robert Czechowski committed
419
        start_server_and_fn(8082, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
420
421
422
423
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
424

425
            let mut resp = login(8082, &client, "testusr", "testpw");
426
            assert_eq!(resp.status(), StatusCode::FOUND);
427

428
            let content = resp.text().unwrap();
429
430
            assert!(!content.contains("Error"));

431
432
433
434
            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
435
436
437
438
            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();
439
440
            assert_eq!(resp.status(), StatusCode::OK);

441
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
442
443
444
445
            assert!(!content.contains("Error"));
            assert!(!content.contains("Gruppenverwaltung"));
            assert!(content.contains("Eingeloggt als <em>testusr</em>"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
446
447
448
        })
    }

449
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
450
    fn check_logout() {
Robert Czechowski's avatar
Robert Czechowski committed
451
        start_server_and_fn(8083, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
452
453
454
455
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
456

457
            let resp = login(8083, &client, "testusr", "testpw");
458
459
460
461
462
463
464
            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);
465
466

            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
467
468
469
470
            assert!(content.contains("Benutzername"));
            assert!(content.contains("Passwort"));
            assert!(content.contains("Gruppencode / Teilnahmecode"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
471
472
473
        })
    }

474
475
    #[test]
    fn check_group_creation_and_group_code_login() {
Robert Czechowski's avatar
Robert Czechowski committed
476
        start_server_and_fn(8084, Some(("testusr".to_string(), "testpw".to_string(), true)), || {
477
478
479
480
481
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

482
            let resp = login(8084, &client, "testusr", "testpw");
483
            assert_eq!(resp.status(), StatusCode::FOUND);
484

485
486
            let mut resp = client.get("http://localhost:8084").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);
487
488

            let content = resp.text().unwrap();
489
490
            assert!(content.contains("[Lehrer]"));
            assert!(content.contains("Gruppenverwaltung"));
491
492
493

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

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

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

502
503
504
            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)];
505
506
            let resp = client.post("http://localhost:8084/group/").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
507
508
509
510

            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
511

512
513
514
515
516
            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
517
518
519
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
520
521
522
523

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

Robert Czechowski's avatar
Robert Czechowski committed
524
525
526
527
528
529
530
531
            let mut set_cookie = resp.headers().get_all("Set-Cookie").iter();
            assert!(set_cookie.next().is_some());
            assert!(set_cookie.next().is_none());

            let location = resp.headers().get(reqwest::header::LOCATION).unwrap().to_str().unwrap();
            assert_eq!(location, "http://localhost:8084/profile");

            let mut resp = client.get(location).send().unwrap();
532
533
534
535
536
537
538
            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
539
540
541
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
542
543
544
545

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

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

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

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

591
592
593
            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
594
595
            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
596
597
598
599
600
601
602

            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>"));
603
604
605
606
607
        })
    }

    #[test]
    fn check_task_load_save() {
608
        start_server_and_fn(8086, None, || {
609
610
611
612
613
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

614
            let resp = client.get("http://localhost:8086/contest/3").send().unwrap();
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
            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");
679
            assert_eq!(resp.status(), StatusCode::FOUND);
Robert Czechowski's avatar
Robert Czechowski committed
680

681
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
682
683
684
685
686
687
            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)];
688
            let resp = client.post("http://localhost:8087/contest/1").form(&params).send().unwrap();
689
690
            assert_eq!(resp.status(), StatusCode::FOUND);

691
            let mut resp = client.get("http://localhost:8087/task/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
692
693
694
695
696
697
            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];

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

Robert Czechowski's avatar
Robert Czechowski committed
701
702
703
            let content = resp.text().unwrap();
            assert_eq!(content, "{}");

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

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

Robert Czechowski's avatar
Robert Czechowski committed
712
713
            let content = resp.text().unwrap();
            assert_eq!(content, "{}");
714

715
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
716
717
718
719
720
            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>"));
721
722

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

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

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, "SomeData");
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>"));
Robert Czechowski's avatar
Robert Czechowski committed
741
742
        })
    }
743
}