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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    contests
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

354
            let mut config = config::read_config_from_file(Path::new("thisfileshoudnotexist"));
355
            config.port = Some(port);
356
            config.cookie_signing_secret = Some("testtesttesttesttesttesttesttest".to_string());
357
            let mut srvr = start_server(conn, config).expect(&format!("Could not start server on port {}", port));
358

359
            // Message server started
360
361
            start_tx.send(()).unwrap();

362
            // Wait for test to finish
363
364
            stop_rx.recv().unwrap();

365
            srvr.close().unwrap();
366
367
        });

368
        // Wait for server to start
369
370
        start_rx.recv().unwrap();
        thread::sleep(time::Duration::from_millis(100));
371
372

        // Run test code
373
        f();
374

375
        // Message test finished
376
377
378
        stop_tx.send(()).unwrap();
    }

379
    fn login(port: u16, client: &reqwest::Client, username: &str, password: &str) -> reqwest::Response {
380
        let params = [("username", username), ("password", password)];
Robert Czechowski's avatar
Robert Czechowski committed
381
        let resp = client.post(&format!("http://localhost:{}/login", port)).form(&params).send().unwrap();
382
        resp
383
    }
Robert Czechowski's avatar
Robert Czechowski committed
384

385
386
387
388
389
    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
    }
390

391
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
392
    fn start_server_and_check_requests() {
Robert Czechowski's avatar
Robert Czechowski committed
393
        start_server_and_fn(8080, None, || {
394
            let mut resp = reqwest::get("http://localhost:8080").unwrap();
395
            assert_eq!(resp.status(), StatusCode::OK);
396
397

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

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

            let content = resp.text().unwrap();
406
407
            assert!(content.contains("<h1>Wettbewerbe</h1>"));
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
408
409

            let mut resp = reqwest::get("http://localhost:8080/group").unwrap();
410
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
411
            assert!(content.contains("<h1>Login</h1>"));
412
413
        })
    }
Daniel Brüning's avatar
Daniel Brüning committed
414

415
416
    #[test]
    fn check_login_wrong_credentials() {
Robert Czechowski's avatar
Robert Czechowski committed
417
        start_server_and_fn(8081, None, || {
418
            let client = reqwest::Client::new();
Robert Czechowski's avatar
Robert Czechowski committed
419

420
            let mut resp = login(8081, &client, "nonexistingusername", "wrongpassword");
421
            assert_eq!(resp.status(), StatusCode::OK);
422
423

            let content = resp.text().unwrap();
424
425
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Login fehlgeschlagen."));
426
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442

            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"));
443
        })
444
    }
445
446

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

454
            let mut resp = login(8082, &client, "testusr", "testpw");
455
            assert_eq!(resp.status(), StatusCode::FOUND);
456

457
            let content = resp.text().unwrap();
458
459
            assert!(!content.contains("Error"));

460
461
462
463
            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
464
465
466
467
            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();
468
469
            assert_eq!(resp.status(), StatusCode::OK);

470
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
471
472
473
474
            assert!(!content.contains("Error"));
            assert!(!content.contains("Gruppenverwaltung"));
            assert!(content.contains("Eingeloggt als <em>testusr</em>"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
475
476
477
        })
    }

478
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
479
    fn check_logout() {
Robert Czechowski's avatar
Robert Czechowski committed
480
        start_server_and_fn(8083, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
481
482
483
484
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
485

486
            let resp = login(8083, &client, "testusr", "testpw");
487
488
489
490
491
492
493
            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);
494
495

            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
496
497
498
499
            assert!(content.contains("Benutzername"));
            assert!(content.contains("Passwort"));
            assert!(content.contains("Gruppencode / Teilnahmecode"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
500
501
502
        })
    }

503
504
    #[test]
    fn check_group_creation_and_group_code_login() {
Robert Czechowski's avatar
Robert Czechowski committed
505
        start_server_and_fn(8084, Some(("testusr".to_string(), "testpw".to_string(), true)), || {
506
507
508
509
510
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

511
            let resp = login(8084, &client, "testusr", "testpw");
512
            assert_eq!(resp.status(), StatusCode::FOUND);
513

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

            let content = resp.text().unwrap();
518
519
            assert!(content.contains("[Lehrer]"));
            assert!(content.contains("Gruppenverwaltung"));
520
521
522

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

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

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

531
532
533
            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)];
534
535
            let resp = client.post("http://localhost:8084/group/").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
536
537
538
539

            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
540

541
542
543
544
545
            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
546
547
548
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
549
550
551
552

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

Robert Czechowski's avatar
Robert Czechowski committed
553
554
555
556
557
            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();
558
            assert_eq!(location, "http://localhost:8084/profile?status=firstlogin");
Robert Czechowski's avatar
Robert Czechowski committed
559
560

            let mut resp = client.get(location).send().unwrap();
561
562
563
564
565
566
567
            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
568
569
570
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
571
572
573
574

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

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

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

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

620
621
622
            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
623
624
            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
625
626
627
628
629
630
631

            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>"));
632
633
634
635
636
        })
    }

    #[test]
    fn check_task_load_save() {
637
        start_server_and_fn(8086, None, || {
638
639
640
641
642
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

643
            let resp = client.get("http://localhost:8086/contest/3").send().unwrap();
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
            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");
708
            assert_eq!(resp.status(), StatusCode::FOUND);
Robert Czechowski's avatar
Robert Czechowski committed
709

710
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
711
712
713
714
715
716
            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)];
717
            let resp = client.post("http://localhost:8087/contest/1").form(&params).send().unwrap();
718
719
            assert_eq!(resp.status(), StatusCode::FOUND);

720
            let mut resp = client.get("http://localhost:8087/task/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
721
722
723
724
725
726
            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];

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
732
            let content = resp.text().unwrap();
            assert_eq!(content, "{}");

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

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

Robert Czechowski's avatar
Robert Czechowski committed
741
742
            let content = resp.text().unwrap();
            assert_eq!(content, "{}");
743

744
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
745
746
747
748
749
            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>"));
750
751

            let params = [("data", "SomeData"), ("grade", "2"), ("csrf_token", csrf)];
752
            let mut resp = client.post("http://localhost:8087/save/1").form(&params).send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
753
754
755
756
757
            assert_eq!(resp.status(), StatusCode::OK);

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

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

Robert Czechowski's avatar
Robert Czechowski committed
761
762
            let content = resp.text().unwrap();
            assert_eq!(content, "SomeData");
763

764
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
765
766
767
768
769
            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
770
771
        })
    }
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796

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