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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    contests
}

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

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

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

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

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

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

    let logincode: String = thread_rng().sample_iter(&Alphanumeric)
                                       .filter(|x| {
                                           let x = *x;
                                           !(x == 'l' || x == 'I' || x == '1' || x == 'O' || x == 'o' || x == '0')
                                       })
                                       .take(8)
                                       .collect();
    let logincode = format!("a{}", logincode);
    print!(" logincode:'{}' …", &logincode);
144
145

    admin.username = Some("admin".into());
146
    admin.logincode = Some(logincode);
147
    match admin.set_password(&password) {
148
        None => println!(" FAILED! (Password hashing error)"),
149
150
        _ => {
            conn.save_session(admin);
151
            println!(" Done");
152
        }
153
154
155
    }
}

156
157
158
159
160
161
162
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) {
163
        print!("Scanning for contests …");
164
        refresh_all_contests(&mut conn);
165
        println!(" Done")
166
167
168
169
170
    }

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

171
        #[cfg(feature = "webbrowser")]
172
        let self_url = config.self_url.clone();
173
        #[cfg(feature = "webbrowser")]
174
175
        let open_browser = config.open_browser;

176
        match start_server(conn, config) {
177
178
            Ok(_) => {
                println!("Server started");
179

180
181
182
183
184
                #[cfg(feature = "webbrowser")]
                {
                    if let (Some(self_url), Some(true)) = (self_url, open_browser) {
                        open_browser_window(&self_url);
                    }
185
                }
186
            }
187
188
            Err(_) => println!("Error on server start …"),
        };
189

190
191
192
193
        println!("Could not run server. Is the port already in use?");
    }
}

194
#[cfg(feature = "webbrowser")]
195
196
197
fn open_browser_window(self_url: &str) {
    match webbrowser::open(&self_url) {
        Ok(_) => (),
198
        Err(e) => println!("Error while opening webbrowser: {:?}", e),
199
200
201
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
202
fn main() {
203
    let opt = config::Opt::from_args();
204
205
206

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

208
    let mut config = config::read_config_from_file(&opt.configfile);
209

210
211
212
213
214
215
    #[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));
216
    opt.teacherpage.map(|x| config.teacher_page = Some(x));
217
218
219
    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 };
220
    config.disable_results_page = if opt.disableresultspage { Some(true) } else { config.disable_results_page };
221
222
223
224
225
226

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

228
229
230
231
232
233
234
235
236
237
    #[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;
        }
238
    }
239
240
241
242
243
244
245
246
247
248
249
250
251
252

    #[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.");
253
}
254
255
256
257

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

Robert Czechowski's avatar
Robert Czechowski committed
260
261
    fn start_server_and_fn<F>(port: u16, set_user: Option<(String, String, bool)>, f: F)
        where F: Fn() {
262
        use std::sync::mpsc::channel;
Robert Czechowski's avatar
Robert Czechowski committed
263
        use std::{thread, time};
264
265
266
267
        let (start_tx, start_rx) = channel();
        let (stop_tx, stop_rx) = channel();

        thread::spawn(move || {
268
            let mut conn = rusqlite::Connection::open_in_memory().unwrap();
269
270
            db_apply_migrations::test(&mut conn);

271
            if let Some(user) = set_user {
272
                let mut test_user = conn.new_session("");
273
                test_user.username = Some(user.0);
Robert Czechowski's avatar
Robert Czechowski committed
274
275
276
                test_user.is_teacher = user.2;
                test_user.set_password(&user.1).expect("Set Password did not work correctly.");
                conn.save_session(test_user);
277
278
            }

279
            // ID: 1, gets renamed
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(),
                                           "RenamedContestName".to_string(),
                                           1,
                                           true,
                                           None,
286
287
                                           None,
                                           None,
288
                                           None,
289
290
                                           None,
                                           None,
Robert Czechowski's avatar
Robert Czechowski committed
291
292
293
                                           None);
            contest.save(&conn);

294
            // ID: 1
Robert Czechowski's avatar
Robert Czechowski committed
295
296
297
298
299
300
            let mut contest = Contest::new("directory".to_string(),
                                           "public.yaml".to_string(),
                                           "PublicContestName".to_string(),
                                           1,
                                           true,
                                           None,
301
302
                                           None,
                                           None,
303
                                           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: 1
Robert Czechowski's avatar
Robert Czechowski committed
309
            taskgroup.tasks.push(task);
310
            let task = Task::new("taskdir2".to_string(), 4); // ID: 2
Robert Czechowski's avatar
Robert Czechowski committed
311
312
313
314
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

315
            // ID: 2
Robert Czechowski's avatar
Robert Czechowski committed
316
317
318
319
320
321
            let mut contest = Contest::new("directory".to_string(),
                                           "private.yaml".to_string(),
                                           "PrivateContestName".to_string(),
                                           1,
                                           false,
                                           None,
322
323
                                           None,
                                           None,
324
                                           None,
325
326
                                           None,
                                           None,
Robert Czechowski's avatar
Robert Czechowski committed
327
                                           None);
328
            let mut taskgroup = Taskgroup::new("TaskgroupName".to_string(), None);
329
            let task = Task::new("taskdir1".to_string(), 3); // ID: 3
Robert Czechowski's avatar
Robert Czechowski committed
330
            taskgroup.tasks.push(task);
331
            let task = Task::new("taskdir2".to_string(), 4); // ID: 4
Robert Czechowski's avatar
Robert Czechowski committed
332
333
334
335
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

336
            // ID: 3
Robert Czechowski's avatar
Robert Czechowski committed
337
338
339
340
341
342
            let mut contest = Contest::new("directory".to_string(),
                                           "infinte.yaml".to_string(),
                                           "InfiniteContestName".to_string(),
                                           0,
                                           true,
                                           None,
343
344
                                           None,
                                           None,
345
                                           None,
346
347
                                           None,
                                           None,
Robert Czechowski's avatar
Robert Czechowski committed
348
                                           None);
349
350
351
352
353
354
355
356
357
            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);
358
            let task = Task::new("taskdir1".to_string(), 3); // ID: 5
Robert Czechowski's avatar
Robert Czechowski committed
359
            taskgroup.tasks.push(task);
360
            let task = Task::new("taskdir2".to_string(), 4); // ID: 6
Robert Czechowski's avatar
Robert Czechowski committed
361
362
363
364
            taskgroup.tasks.push(task);
            contest.taskgroups.push(taskgroup);
            contest.save(&conn);

365
            let mut config = config::read_config_from_file(Path::new("thisfileshoudnotexist"));
366
            config.port = Some(port);
367
            config.cookie_signing_secret = Some("testtesttesttesttesttesttesttest".to_string());
368
            let mut srvr = start_server(conn, config).expect(&format!("Could not start server on port {}", port));
369

370
            // Message server started
371
372
            start_tx.send(()).unwrap();

373
            // Wait for test to finish
374
375
            stop_rx.recv().unwrap();

376
            srvr.close().unwrap();
377
378
        });

379
        // Wait for server to start
380
381
        start_rx.recv().unwrap();
        thread::sleep(time::Duration::from_millis(100));
382
383

        // Run test code
384
        f();
385

386
        // Message test finished
387
388
389
        stop_tx.send(()).unwrap();
    }

390
    fn login(port: u16, client: &reqwest::Client, username: &str, password: &str) -> reqwest::Response {
391
        let params = [("username", username), ("password", password)];
Robert Czechowski's avatar
Robert Czechowski committed
392
        let resp = client.post(&format!("http://localhost:{}/login", port)).form(&params).send().unwrap();
393
        resp
394
    }
Robert Czechowski's avatar
Robert Czechowski committed
395

396
397
398
399
400
    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
    }
401

402
    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
403
    fn start_server_and_check_requests() {
Robert Czechowski's avatar
Robert Czechowski committed
404
        start_server_and_fn(8080, None, || {
405
            let mut resp = reqwest::get("http://localhost:8080").unwrap();
406
            assert_eq!(resp.status(), StatusCode::OK);
407
408

            let content = resp.text().unwrap();
409
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
410
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
411
            assert!(!content.contains("Gruppenverwaltung"));
412
413

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

            let content = resp.text().unwrap();
417
418
            assert!(content.contains("<h1>Wettbewerbe</h1>"));
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
419
420

            let mut resp = reqwest::get("http://localhost:8080/group").unwrap();
421
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
422
            assert!(content.contains("<h1>Login</h1>"));
423
424
        })
    }
Daniel Brüning's avatar
Daniel Brüning committed
425

426
427
    #[test]
    fn check_login_wrong_credentials() {
Robert Czechowski's avatar
Robert Czechowski committed
428
        start_server_and_fn(8081, None, || {
429
            let client = reqwest::Client::new();
Robert Czechowski's avatar
Robert Czechowski committed
430

431
            let mut resp = login(8081, &client, "nonexistingusername", "wrongpassword");
432
            assert_eq!(resp.status(), StatusCode::OK);
433
434

            let content = resp.text().unwrap();
435
436
            assert!(content.contains("<h1>Login</h1>"));
            assert!(content.contains("Login fehlgeschlagen."));
437
            assert!(!content.contains("Error"));
Robert Czechowski's avatar
Robert Czechowski committed
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453

            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"));
454
        })
455
    }
456
457

    #[test]
Robert Czechowski's avatar
Robert Czechowski committed
458
    fn check_login() {
Robert Czechowski's avatar
Robert Czechowski committed
459
        start_server_and_fn(8082, Some(("testusr".to_string(), "testpw".to_string(), false)), || {
460
461
462
463
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
464

465
            let mut resp = login(8082, &client, "testusr", "testpw");
466
            assert_eq!(resp.status(), StatusCode::FOUND);
467

468
            let content = resp.text().unwrap();
469
470
            assert!(!content.contains("Error"));

471
472
473
474
            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
475
476
477
478
            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();
479
480
            assert_eq!(resp.status(), StatusCode::OK);

481
            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
482
483
484
485
            assert!(!content.contains("Error"));
            assert!(!content.contains("Gruppenverwaltung"));
            assert!(content.contains("Eingeloggt als <em>testusr</em>"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
486
487
488
        })
    }

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

497
            let resp = login(8083, &client, "testusr", "testpw");
498
499
500
501
502
503
504
            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);
505
506

            let content = resp.text().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
507
508
509
510
            assert!(content.contains("Benutzername"));
            assert!(content.contains("Passwort"));
            assert!(content.contains("Gruppencode / Teilnahmecode"));
            assert!(content.contains("Jugendwettbewerb Informatik</h1>"));
511
512
513
        })
    }

514
515
    #[test]
    fn check_group_creation_and_group_code_login() {
Robert Czechowski's avatar
Robert Czechowski committed
516
        start_server_and_fn(8084, Some(("testusr".to_string(), "testpw".to_string(), true)), || {
517
518
519
520
521
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

522
            let resp = login(8084, &client, "testusr", "testpw");
523
            assert_eq!(resp.status(), StatusCode::FOUND);
524

525
526
            let mut resp = client.get("http://localhost:8084").send().unwrap();
            assert_eq!(resp.status(), StatusCode::OK);
527
528

            let content = resp.text().unwrap();
529
530
            assert!(content.contains("[Lehrer]"));
            assert!(content.contains("Gruppenverwaltung"));
531
532
533

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

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

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

542
543
544
            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)];
545
546
            let resp = client.post("http://localhost:8084/group/").form(&params).send().unwrap();
            assert_eq!(resp.status(), StatusCode::FOUND);
547
548
549
550

            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
551

552
553
554
555
556
            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
557
558
559
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
560
561
562
563

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

Robert Czechowski's avatar
Robert Czechowski committed
564
565
566
567
568
            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();
569
            assert_eq!(location, "http://localhost:8084/profile?status=firstlogin");
Robert Czechowski's avatar
Robert Czechowski committed
570
571

            let mut resp = client.get(location).send().unwrap();
572
573
574
575
576
577
578
            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
579
580
581
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();
582
583
584
585

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

Robert Czechowski's avatar
Robert Czechowski committed
586
587
588
589
            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();
590
591
            let content = resp.text().unwrap();
            assert!(content.contains("Eingeloggt als <em></em>"));
592
593
        })
    }
Robert Czechowski's avatar
Robert Czechowski committed
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626

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

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

631
632
633
            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
634
635
            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
636
637
638
639
640
641
642

            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>"));
643
644
645
646
647
        })
    }

    #[test]
    fn check_task_load_save() {
648
        start_server_and_fn(8086, None, || {
649
650
651
652
653
            let client = reqwest::Client::builder().cookie_store(true)
                                                   .redirect(reqwest::RedirectPolicy::none())
                                                   .build()
                                                   .unwrap();

654
            let resp = client.get("http://localhost:8086/contest/3").send().unwrap();
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
708
709
710
711
712
713
714
715
716
717
718
            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");
719
            assert_eq!(resp.status(), StatusCode::FOUND);
Robert Czechowski's avatar
Robert Czechowski committed
720

721
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
722
723
724
725
726
727
            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)];
728
            let resp = client.post("http://localhost:8087/contest/1").form(&params).send().unwrap();
729
730
            assert_eq!(resp.status(), StatusCode::FOUND);

731
            let mut resp = client.get("http://localhost:8087/task/1").send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
732
733
734
735
736
737
            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];

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

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

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

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

755
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
756
757
758
759
760
            assert_eq!(resp.status(), StatusCode::OK);

            let content = resp.text().unwrap();
            assert!(content.contains("<a href=\"/task/1\">☆☆☆</a></li>"));
            assert!(content.contains("<a href=\"/task/2\">☆☆☆☆</a></li>"));
761
762

            let params = [("data", "SomeData"), ("grade", "2"), ("csrf_token", csrf)];
763
            let mut resp = client.post("http://localhost:8087/save/1").form(&params).send().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
764
765
766
767
768
            assert_eq!(resp.status(), StatusCode::OK);

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

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

Robert Czechowski's avatar
Robert Czechowski committed
772
773
            let content = resp.text().unwrap();
            assert_eq!(content, "SomeData");
774

775
            let mut resp = client.get("http://localhost:8087/contest/1").send().unwrap();
776
777
778
779
780
            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
781
782
        })
    }
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807

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