webfw_iron.rs 28.5 KB
Newer Older
Robert Czechowski's avatar
Robert Czechowski committed
1
2
3
4
5
6
//extern crate serde;

use std::path::Path;

use iron_sessionstorage::traits::*;
use iron::prelude::*;
7

Robert Czechowski's avatar
Robert Czechowski committed
8
9
use iron::{status, AfterMiddleware};
use iron::modifiers::Redirect;
10
use iron::modifiers::RedirectRaw;
Robert Czechowski's avatar
Robert Czechowski committed
11
12
13
14
15
16
17
18

use mount::Mount;
use router::Router;
use staticfile::Static;

use iron_sessionstorage::SessionStorage;
use iron_sessionstorage::backends::SignedCookieBackend;
use rusqlite::Connection;
19
use urlencoded::{UrlEncodedBody, UrlEncodedQuery};
Robert Czechowski's avatar
Robert Czechowski committed
20
21
22
23
24
use persistent::Write;

use handlebars_iron::{HandlebarsEngine,DirectorySource,Template};
pub use handlebars_iron::handlebars::to_json;

25
26
27
28
29
use iron_sessionstorage;
use iron;
use reqwest;
use rusqlite;

Robert Czechowski's avatar
Robert Czechowski committed
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
pub use serde_json::value as json_val;

use iron::typemap::Key;

static TASK_DIR: &'static str = "tasks";

macro_rules! mime {
    ($top:tt / $sub:tt) => (
        mime!($top / $sub;)
    );

    ($top:tt / $sub:tt ; $($attr:tt = $val:tt),*) => (
        iron::mime::Mime(
            iron::mime::TopLevel::$top,
            iron::mime::SubLevel::$sub,
            vec![ $((Attr::$attr,Value::$val)),* ]
        )
    );
}

50
51
52
53
54
55
56
57
58
macro_rules! with_conn {
    ( $x:expr , $r:expr , $($y:expr),* ) => {
        {
            let mutex = $r.get::<Write<SharedDatabaseConnection>>().unwrap();
            let conn = mutex.lock().unwrap_or_else(|e| e.into_inner());
            $x(&*conn, $($y),*)
        }
    };
}
Robert Czechowski's avatar
Robert Czechowski committed
59
60
61
62
63
64
65
66
67
68


struct ErrorReporter;
impl AfterMiddleware for ErrorReporter {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        println!("{}", err);
        Err(err)
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
69
#[derive(Debug)]
Robert Czechowski's avatar
Robert Czechowski committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
struct SessionToken {
    token: String
}
impl iron_sessionstorage::Value for SessionToken {
    fn get_key() -> &'static str { "medal_session" }
    fn into_raw(self) -> String { self.token }
    fn from_raw(value: String) -> Option<Self> {
        if value.is_empty() {
            None
        } else {
            Some(SessionToken { token: value })
        }
    }
}


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use ::iron::middleware::{AroundMiddleware,Handler};
use rand::{thread_rng, Rng, distributions::Alphanumeric};

pub struct CookieDistributor {}

impl CookieDistributor {
    pub fn new() -> Self { Self {} }
}

impl AroundMiddleware for CookieDistributor {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(move |req: &mut Request| -> IronResult<Response> {
            if !req.session().get::<SessionToken>().expect("blub...").is_some() {
                let session_token: String = thread_rng().sample_iter(&Alphanumeric).take(10).collect();
                req.session().set(SessionToken { token: session_token }).unwrap();
            }
            handler.handle(req)
        })
    }

}


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#[derive(Debug)]
struct SessionError {
    message: String
}
impl ::std::error::Error for SessionError {
    fn description(&self) -> &str {
        &self.message
    }
}

impl ::std::fmt::Display for SessionError {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{}", self.message)
    }
}

125
126
127
128
trait RequestSession {
    fn get_session_token(&mut self) -> Option<String>;
    fn require_session_token(&mut self) -> IronResult<String>;
    fn expect_session_token(&mut self) -> IronResult<String>;
129
130
}

131
132
impl<'a, 'b> RequestSession for Request<'a, 'b> {
    fn get_session_token(self: &mut Self) -> Option<String> {
133
        let session_token = self.session().get::<SessionToken>().unwrap();
134
135
136
137
        (|st: Option<SessionToken>| -> Option<String> {Some(st?.token)}) (session_token)
    }

    fn require_session_token(self: &mut Self) -> IronResult<String> {
138
        match self.session().get::<SessionToken>().unwrap() {
139
140
            Some(SessionToken { token: session }) => Ok(session),
            _ => {
141
142

                use rand::{thread_rng, Rng};
143

Daniel Brüning's avatar
Daniel Brüning committed
144
                let new_session_key: String = thread_rng().sample_iter(&Alphanumeric).take(28).collect();
145
                self.session().set(SessionToken { token: new_session_key }).unwrap();
146
147
148
149
150
151
152
                Err(IronError { error: Box::new(SessionError { message: "No valid session found, redirecting to cookie page".to_string() }),
                                response: Response::with((status::Found, RedirectRaw(format!("/cookie?{}", self.url.path().join("/"))))) })
            }
        }
    }

    fn expect_session_token(self: &mut Self) -> IronResult<String> {
153
        match self.session().get::<SessionToken>().unwrap() {
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
            Some(SessionToken { token: session }) => Ok(session),
            _ => {
                Err(IronError { error: Box::new(SessionError { message: "No valid session found, access denied".to_string() }),
                                response: Response::with(status::Forbidden) })
            }
        }
    }
}

trait RequestRouterParam {
    fn get_str(self: &mut Self, key: &str) -> Option<String>;
    fn get_int<T: ::std::str::FromStr>(self: &mut Self, key: &str) -> Option<T>;
    fn expect_int<T: ::std::str::FromStr>(self: &mut Self, key: &str) -> IronResult<T>;
}

impl<'a, 'b> RequestRouterParam for Request<'a, 'b> {
    fn get_str(self: &mut Self, key: &str) -> Option<String> {
        Some(self.extensions.get::<Router>()?.find(key)?.to_owned())
    }

    fn get_int<T: ::std::str::FromStr>(self: &mut Self, key: &str) -> Option<T> {
        Some(self.extensions.get::<Router>()?.find(key)?.parse::<T>().ok()?)
    }
177

178
179
180
181
182
183
184
    fn expect_int<T: ::std::str::FromStr>(self: &mut Self, key: &str) -> IronResult<T> {
        match self.get_int::<T>(key) {
            Some(i) => Ok(i),
            _ => {
            Err(IronError { error: Box::new(SessionError { message: "No valid routing parameter".to_string() }),
                            response: Response::with(status::Forbidden) })
            }
185
186
187
188
189
        }
    }
}


Robert Czechowski's avatar
Robert Czechowski committed
190
191
use ::functions;

192
193
194
195
196
197
198
struct AugMedalError<'c, 'a: 'c, 'b: 'c + 'a>(functions::MedalError, &'c mut Request<'a, 'b>);

impl<'c, 'a, 'b> From<AugMedalError<'c, 'a, 'b>> for IronError {
    fn from(AugMedalError(me, req): AugMedalError<'c, 'a, 'b>) -> Self {
        match me {
            functions::MedalError::NotLoggedIn => IronError {
                error: Box::new(SessionError { message: "Not Logged in, redirecting to login page".to_string() }),
199
200
201
                response: Response::with((status::Found, RedirectRaw(format!("/login?{}", req.url.path().join("/"))))) },
            functions::MedalError::AccessDenied => IronError {
                error: Box::new(SessionError { message: "Access denied".to_string() }),
202
203
204
205
206
207
208
209
210
211
                response: Response::with(status::Forbidden) },
            functions::MedalError::CsrfCheckFailed => IronError {
                error: Box::new(SessionError { message: "CSRF Error".to_string() }),
                response: Response::with(status::Forbidden) },
            functions::MedalError::SessionTimeout => IronError {
                error: Box::new(SessionError { message: "Session timed out".to_string() }),
                response: Response::with(status::Forbidden) },
            functions::MedalError::DatabaseError => IronError {
                error: Box::new(SessionError { message: "Database Error".to_string() }),
                response: Response::with(status::Forbidden) },
212
213
            functions::MedalError::PasswordHashingError => IronError {
                error: Box::new(SessionError { message: "Error hashing the passwords".to_string() }),
214
                response: Response::with(status::Forbidden) },
215
216
217
            functions::MedalError::UnmatchedPasswords => IronError {
                error: Box::new(SessionError { message: "The two passwords did not match.".to_string() }),
                response: Response::with(status::Forbidden) },
218
        }
219
220
221
    }
}

222
223
224
225
226
227
228
229
230
231

trait RequestAugmentMedalError<'c, 'a: 'c, 'b: 'c + 'a, R> {
    fn aug(self, req: &'c mut Request<'a, 'b>) -> Result<R, AugMedalError<'c, 'a, 'b>>;
}
impl<'c, 'a: 'c, 'b: 'c + 'a, T> RequestAugmentMedalError<'c, 'a, 'b, T> for Result<T, functions::MedalError>  {
    fn aug(self, req: &'c mut Request<'a, 'b>) -> Result<T, AugMedalError<'c, 'a, 'b>> {
        self.map_err(move |me| {AugMedalError(me, req)})
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
232
233

fn greet_personal(req: &mut Request) -> IronResult<Response> {
234
    let session_token = req.get_session_token();
Robert Czechowski's avatar
Robert Czechowski committed
235
236
    // hier ggf. Daten aus dem Request holen

237
238
239
    let (self_url, oauth_url) = {
        let mutex = req.get::<Write<SharedConfiguration>>().unwrap();
        let config = mutex.lock().unwrap_or_else(|e| e.into_inner());
240
        (config.self_url.clone(), config.oauth_url.clone())
241
242
    };

Robert Czechowski's avatar
Robert Czechowski committed
243
244
245
246
247
    let (template, data) = {
        // hier ggf. Daten aus dem Request holen
        let mutex = req.get::<Write<SharedDatabaseConnection>>().unwrap();
        let conn = mutex.lock().unwrap_or_else(|e| e.into_inner());

248
        // Antwort erstellen und zurücksenden
249
        functions::index(&*conn, session_token, (self_url, oauth_url))
Robert Czechowski's avatar
Robert Czechowski committed
250
    };
Robert Czechowski's avatar
Robert Czechowski committed
251
252
    // Daten verarbeiten

253
    // Antwort erstellen und zurücksenden
Robert Czechowski's avatar
Robert Czechowski committed
254
255
256
257
258
259
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

fn contests(req: &mut Request) -> IronResult<Response> {
260
    let (template, data) = with_conn![functions::show_contests, req, ];
261

Robert Czechowski's avatar
Robert Czechowski committed
262
263
264
265
266
267
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

fn contest(req: &mut Request) -> IronResult<Response> {
268
269
    let contest_id    = req.expect_int::<u32>("contestid")?;
    let session_token = req.require_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
270

271
    let (template, data) = with_conn![functions::show_contest, req, contest_id,  session_token].aug(req)?;
Robert Czechowski's avatar
Robert Czechowski committed
272
273
274
275
276
277

    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

278
279
280
281
fn contestresults(req: &mut Request) -> IronResult<Response> {
    let contest_id    = req.expect_int::<u32>("contestid")?;
    let session_token = req.require_session_token()?;

282
    let (template, data) = with_conn![functions::show_contest_results, req, contest_id,  session_token].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
283

284
285
286
287
288
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

Robert Czechowski's avatar
Robert Czechowski committed
289
fn contest_post(req: &mut Request) -> IronResult<Response> {
290
291
    let contest_id    = req.expect_int::<u32>("contestid")?;
    let session_token = req.expect_session_token()?;
292

Robert Czechowski's avatar
Robert Czechowski committed
293
294
295
296
297
    let csrf_token = {
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
        iexpect!(formdata.get("csrftoken"))[0].to_owned()
    };

298
    // TODO: Was mit dem Result?
Daniel Brüning's avatar
Daniel Brüning committed
299
    let _startcontestresult = with_conn![functions::start_contest, req, contest_id, session_token, csrf_token].aug(req)?;
Robert Czechowski's avatar
Robert Czechowski committed
300

301
    Ok(Response::with((status::Found, Redirect(url_for!(req, "contest", "contestid" => format!("{}",contest_id))))))
Robert Czechowski's avatar
Robert Czechowski committed
302
303
}

304
fn login(req: &mut Request) -> IronResult<Response> {
Daniel Brüning's avatar
Daniel Brüning committed
305
    let (self_url, oauth_url) = {
306
307
        let mutex = req.get::<Write<SharedConfiguration>>().unwrap();
        let config = mutex.lock().unwrap_or_else(|e| e.into_inner());
Daniel Brüning's avatar
Daniel Brüning committed
308

309
        (config.self_url.clone(), config.oauth_url.clone())
310
311
312
313
314
    };

    let mut data = json_val::Map::new();
    data.insert("self_url".to_string(), to_json(&self_url));
    data.insert("oauth_url".to_string(), to_json(&oauth_url));
315

Robert Czechowski's avatar
Robert Czechowski committed
316
    let mut resp = Response::new();
317
    resp.set_mut(Template::new("login", data)).set_mut(status::Ok);
Robert Czechowski's avatar
Robert Czechowski committed
318
319
320
321
322
323
324
325
326
327
    Ok(resp)
}

fn login_post(req: &mut Request) -> IronResult<Response> {
    let logindata = {
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
        (iexpect!(formdata.get("username"))[0].to_owned(),
         iexpect!(formdata.get("password"))[0].to_owned())
    };

328
    // TODO: Submit current session to login
Daniel Brüning's avatar
Daniel Brüning committed
329

330
    let loginresult = with_conn![functions::login, req, logindata];
Robert Czechowski's avatar
Robert Czechowski committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

    match loginresult {
        // Login successful
        Ok(sessionkey) => {
            req.session().set(SessionToken { token: sessionkey }).unwrap();
            Ok(Response::with((status::Found, Redirect(url_for!(req, "greet")))))
        },
        // Login failed
        Err((template, data)) => {
            let mut resp = Response::new();
            resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
            Ok(resp)
        }
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
347
348
349
350
351
352
fn login_code_post(req: &mut Request) -> IronResult<Response> {
    let code = {
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
        iexpect!(formdata.get("code"))[0].to_owned()
    };

353
354
    // TODO: Submit current session to login

355
    let loginresult = with_conn![functions::login_with_code, req, code];
Robert Czechowski's avatar
Robert Czechowski committed
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
    println!("aa");

    match loginresult {
        // Login successful
        Ok(Ok(sessionkey)) => {
            req.session().set(SessionToken { token: sessionkey }).unwrap();
            Ok(Response::with((status::Found, Redirect(url_for!(req, "greet")))))
        },
        Ok(Err(sessionkey)) => {
            req.session().set(SessionToken { token: sessionkey }).unwrap();
            Ok(Response::with((status::Found, Redirect(url_for!(req, "profile")))))
        },
        // Login failed
        Err((template, data)) => {
            println!("bb");
            let mut resp = Response::new();
            resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
            Ok(resp)
        }
    }
376
}
Robert Czechowski's avatar
Robert Czechowski committed
377
378

fn logout(req: &mut Request) -> IronResult<Response> {
379
    let session_token = req.get_session_token();
Robert Czechowski's avatar
Robert Czechowski committed
380

381
    println!("Loggin out session {:?}", session_token);
Daniel Brüning's avatar
Daniel Brüning committed
382

383
    with_conn![functions::logout, req, session_token];
Robert Czechowski's avatar
Robert Czechowski committed
384
385
386
387
388

    Ok(Response::with((status::Found, Redirect(url_for!(req, "greet")))))
}


Robert Czechowski's avatar
Robert Czechowski committed
389
fn submission(req: &mut Request) -> IronResult<Response> {
390
391
    let task_id       = req.expect_int::<u32>("taskid")?;
    let session_token = req.expect_session_token()?;
392
393
    let subtask : Option<String> =
        (|| -> Option<String> {req.get_ref::<UrlEncodedQuery>().ok()?.get("subtask")?.get(0).map(|x| x.to_owned())})();
394

Robert Czechowski's avatar
Robert Czechowski committed
395
396
    println!("{}",task_id);

397
    let result = with_conn![functions::load_submission, req, task_id, session_token, subtask];
Robert Czechowski's avatar
Robert Czechowski committed
398
399
400
401
402
403
404
405
406
407
408
409
410
411

    match result {
        Ok(data) => Ok(Response::with((
            status::Ok,
            mime!(Application/Json),
            format!("{}", data)))),
        Err(_) => Ok(Response::with((
            status::BadRequest,
            mime!(Application/Json),
            format!("{{}}"))))
    }
}

fn submission_post(req: &mut Request) -> IronResult<Response> {
412
413
    let task_id       = req.expect_int::<u32>("taskid")?;
    let session_token = req.expect_session_token()?;
414
    let (csrf_token, data, grade, subtask) = {
Robert Czechowski's avatar
Robert Czechowski committed
415
416
        let formdata = iexpect!(req.get_ref::<UrlEncodedBody>().ok());
        (iexpect!(formdata.get("csrf"),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request")))[0].to_owned(),
417
         iexpect!(formdata.get("data"),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request")))[0].to_owned(),
418
         iexpect!(formdata.get("grade").unwrap_or(&vec!["0".to_owned()])[0].parse::<u8>().ok(),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request"))),
419
         formdata.get("subtask").map(|x| x[0].to_owned()),
420
        )
Robert Czechowski's avatar
Robert Czechowski committed
421
422
423
        };
    println!("{}",data);
    println!("{}",task_id);
424
    println!("{}",grade);
Daniel Brüning's avatar
Daniel Brüning committed
425

426
    let result = with_conn![functions::save_submission, req, task_id, session_token, csrf_token, data, grade, subtask];
Daniel Brüning's avatar
Daniel Brüning committed
427

Robert Czechowski's avatar
Robert Czechowski committed
428
429
430
431
432
433
434
435
436
    match result {
        Ok(_) => Ok(Response::with((
            status::Ok,
            mime!(Application/Json),
            format!("{{}}")))),
        Err(_) => Ok(Response::with((
            status::BadRequest,
            mime!(Application/Json),
            format!("{{}}"))))
437
    }
Robert Czechowski's avatar
Robert Czechowski committed
438
439
440
}

fn task(req: &mut Request) -> IronResult<Response> {
441
442
    let task_id       = req.expect_int::<u32>("taskid")?;
    let session_token = req.require_session_token()?;
443

Robert Czechowski's avatar
Robert Czechowski committed
444
445
    println!("{}",task_id);

446
    let (template, data) = with_conn![functions::show_task, req, task_id, session_token].aug(req)?;
Robert Czechowski's avatar
Robert Czechowski committed
447
448
449
450
451
452
453

    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

fn groups(req: &mut Request) -> IronResult<Response> {
454
    let session_token = req.require_session_token()?;
Daniel Brüning's avatar
Daniel Brüning committed
455

456
    let (template, data) = with_conn![functions::show_groups, req, session_token].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
457

Robert Czechowski's avatar
Robert Czechowski committed
458
459
460
461
462
463
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

fn group(req: &mut Request) -> IronResult<Response> {
464
465
    let group_id      = req.expect_int::<u32>("groupid")?;
    let session_token = req.require_session_token()?;
Daniel Brüning's avatar
Daniel Brüning committed
466

467
    let (template, data) = with_conn![functions::show_group, req, group_id, session_token].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
468

469
470
471
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
Robert Czechowski's avatar
Robert Czechowski committed
472
473
474
}

fn group_post(req: &mut Request) -> IronResult<Response> {
475
476
    let group_id      = req.expect_int::<u32>("groupid")?;
    let session_token = req.expect_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
477

Daniel Brüning's avatar
Daniel Brüning committed
478
479
    //TODO: use changegroupresult
    let _changegroupresult = with_conn![functions::modify_group, req, group_id, session_token].aug(req)?;
Robert Czechowski's avatar
Robert Czechowski committed
480

481
    Ok(Response::with((status::Found, Redirect(url_for!(req, "group", "groupid" => format!("{}",group_id))))))
Robert Czechowski's avatar
Robert Czechowski committed
482
483
484
}

fn new_group(req: &mut Request) -> IronResult<Response> {
485
    let session_token = req.require_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
486
487
488
489
490
491
492
493
494
495

    let (csrf, name, tag) = {
        let formdata = iexpect!(req.get_ref::<UrlEncodedBody>().ok());
        (iexpect!(formdata.get("csrf"),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request")))[0].to_owned(),
         iexpect!(formdata.get("name"),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request")))[0].to_owned(),
         iexpect!(formdata.get("tag"),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request")))[0].to_owned())
    };
    println!("{}",csrf);
    println!("{}",name);

496
    let group_id = with_conn![functions::add_group, req, session_token, csrf, name, tag].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
497

498
    Ok(Response::with((status::Found, Redirect(url_for!(req, "group", "groupid" => format!("{}",group_id))))))
Robert Czechowski's avatar
Robert Czechowski committed
499
500
501
}

fn profile(req: &mut Request) -> IronResult<Response> {
502
    let session_token = req.require_session_token()?;
503
    let query_string =  req.url.query().map(|s| s.to_string());
Daniel Brüning's avatar
Daniel Brüning committed
504

505
    let (template, data) = with_conn![functions::show_profile, req, session_token, None, query_string].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
506

Robert Czechowski's avatar
Robert Czechowski committed
507
508
509
510
511
512
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

fn profile_post(req: &mut Request) -> IronResult<Response> {
513
    let session_token = req.expect_session_token()?;
514
    let (csrf_token, firstname, lastname, pwd, pwd_repeat, grade) = {
Robert Czechowski's avatar
Robert Czechowski committed
515
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
516
517
518
519
        (
            iexpect!(formdata.get("csrftoken"))[0].to_owned(),
            iexpect!(formdata.get("firstname"))[0].to_owned(),
            iexpect!(formdata.get("lastname"))[0].to_owned(),
520
521
            iexpect!(formdata.get("password"))[0].to_owned(),
            iexpect!(formdata.get("password_repeat"))[0].to_owned(),
522
523
            iexpect!(formdata.get("grade"))[0].parse::<u8>().unwrap_or(0)
        )
Robert Czechowski's avatar
Robert Czechowski committed
524
    };
Daniel Brüning's avatar
Daniel Brüning committed
525
526
527

    //TODO: use profilechangeresult
    let _profilechangeresult = with_conn![functions::edit_profile, req, session_token, None, csrf_token, firstname, lastname, pwd, pwd_repeat, grade].aug(req)?;
528

529
    Ok(Response::with((status::Found, Redirect(url_for!(req, "profile")))))
Robert Czechowski's avatar
Robert Czechowski committed
530
531
532
}

fn user(req: &mut Request) -> IronResult<Response> {
533
534
    let user_id = req.expect_int::<u32>("userid")?;
    let session_token = req.expect_session_token()?;
535
    let query_string =  req.url.query().map(|s| s.to_string());
Daniel Brüning's avatar
Daniel Brüning committed
536

537
    let (template, data) = with_conn![functions::show_profile, req, session_token, Some(user_id), query_string].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
538

Robert Czechowski's avatar
Robert Czechowski committed
539
540
541
542
543
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

544
545
546
fn user_post(req: &mut Request) -> IronResult<Response> {
    let user_id = req.expect_int::<u32>("userid")?;
    let session_token = req.expect_session_token()?;
547
    let (csrf_token, firstname, lastname, pwd, pwd_repeat, grade) = {
548
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
549
550
551
552
        (
            iexpect!(formdata.get("csrftoken"))[0].to_owned(),
            iexpect!(formdata.get("firstname"))[0].to_owned(),
            iexpect!(formdata.get("lastname"))[0].to_owned(),
553
554
            iexpect!(formdata.get("password"))[0].to_owned(),
            iexpect!(formdata.get("password_repeat"))[0].to_owned(),
555
556
557
            iexpect!(formdata.get("grade"))[0].parse::<u8>().unwrap_or(0),
        )

558
    };
Daniel Brüning's avatar
Daniel Brüning committed
559
560
561

    //TODO: use profilechangeresult
    let _profilechangeresult  = with_conn![functions::edit_profile, req, session_token, Some(user_id), csrf_token, firstname, lastname, pwd, pwd_repeat, grade].aug(req)?;
562
563
564
565
566

    Ok(Response::with((status::Found, Redirect(url_for!(req, "user", "userid" => format!("{}",user_id))))))
}


567
568
569
570
571
572
573
574
575
576
#[derive(Deserialize, Debug)]
struct OAuthAccess {
    access_token:  String,
    token_type:    String,
    refresh_token: String,
    expires:       Option<u32>, // documented as 'expires_in'
    expires_in:    Option<u32>, // sent as 'expires'
}

#[derive(Deserialize, Debug)]
577
#[allow(non_snake_case)]
578
pub struct OAuthUserData {
579
580
581
582
583
584
    userID:      Option<String>, // documented as 'userId'
    userId:      Option<String>, // sent as 'userID'
    userType:    String,
    gender:      String,
    firstName:   String,
    lastName:    String,
585
    dateOfBirth: Option<String>,
586
587
588
589
590
591
592
593
594
595
    eMail:       Option<String>,
    userId_int:  Option<u32>,
}

fn oauth(req: &mut Request) -> IronResult<Response> {
    use reqwest::header;
    use params::{Params, Value};

    let (client_id, client_secret, access_token_url, user_data_url) = {
        let mutex = req.get::<Write<SharedConfiguration>>().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
596
        let config = mutex.lock().unwrap_or_else(|e| e.into_inner());
597
598
599
600
601
602
603
        if let (Some(id), Some(secret), Some(atu), Some(udu))
            = (&config.oauth_client_id, &config.oauth_client_secret, &config.oauth_access_token_url, &config.oauth_user_data_url) {
            (id.clone(), secret.clone(), atu.clone(), udu.clone())
        } else {
            return Ok(Response::with(iron::status::NotFound))
        }
    };
604

605
    let (_state, _scope, code): (String, String, String) = {
606
607
608
609
610
611
612
613
614
615
        let map = req.get_ref::<Params>().unwrap();

        match (map.find(&["state"]),map.find(&["scope"]),map.find(&["code"])) {
            (Some(&Value::String(ref state)),Some(&Value::String(ref scope)),Some(&Value::String(ref code))) if state == "42" => {
                (state.clone(), scope.clone(), code.clone())
            },
            _ => return Ok(Response::with(iron::status::NotFound)),
        }
    };

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
    let client = reqwest::Client::new().unwrap();
    let params = [("code", code), ("grant_type", "authorization_code".to_string())];
    let res = client.post(&access_token_url)
        .header(header::Authorization(header::Basic {
            username: client_id,
            password: Some(client_secret)}))
        .form(&params)
        .send();
    let access: OAuthAccess = res.expect("network error").json().expect("malformed json");

    let res = client.post(&user_data_url)
        .header(header::Authorization(header::Bearer{token: access.access_token}))
        .form(&params)
        .send();
    let mut user_data: OAuthUserData = res.expect("network error").json().expect("malformed json");

    if let Some(ref id) = user_data.userID {
        user_data.userId_int = Some(id.parse::<u32>().unwrap());
    }
    if let Some(ref id) = user_data.userId {
        user_data.userId_int = Some(id.parse::<u32>().unwrap());
    }

    use functions::{UserType, UserGender};

    let user_data = functions::ForeignUserData {
        foreign_id:   user_data.userId_int.unwrap(),
        foreign_type: match user_data.userType.as_ref() {
            "a" | "A"     => UserType::Admin,
            "t" | "T"     => UserType::Teacher,
            "s" | "S" | _ => UserType::User,
        },
        gender: match user_data.gender.as_ref() {
            "m" | "M"             => UserGender::Male,
            "f" | "F" | "w" | "W" => UserGender::Female,
            "?" | _               => UserGender::Unknown,
        },
        firstname:   user_data.firstName,
        lastname:    user_data.lastName,
    };

657

658
659
660
661
    let oauthloginresult  = {
        // hier ggf. Daten aus dem Request holen
        let mutex = req.get::<Write<SharedDatabaseConnection>>().unwrap();
        let conn = mutex.lock().unwrap_or_else(|e| e.into_inner());
662
663

        // Antwort erstellen und zurücksenden
664
665
666
667
668
        functions::login_oauth(&*conn, user_data)
        /*let mut data = json_val::Map::new();
            data.insert("reason".to_string(), to_json(&"Not implemented".to_string()));
            ("profile", data)*/
    };
669

670
671
672
673
674
675
676
677
678
679
680
681
682
    match oauthloginresult {
        // Login successful
        Ok(sessionkey) => {
            req.session().set(SessionToken { token: sessionkey }).unwrap();
            Ok(Response::with((status::Found, Redirect(url_for!(req, "greet")))))
        },
        // Login failed
        Err((template, data)) => {
            let mut resp = Response::new();
            resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
            Ok(resp)
        }
    }
683
684
}

Robert Czechowski's avatar
Robert Czechowski committed
685

Robert Czechowski's avatar
Robert Czechowski committed
686
687
688
689
690
// Share Database connection between workers
#[derive(Copy, Clone)]
pub struct SharedDatabaseConnection;
impl Key for SharedDatabaseConnection { type Value = rusqlite::Connection; }

691
692
693
694
695
// Share Configuration between workers
#[derive(Copy, Clone)]
pub struct SharedConfiguration;
impl Key for SharedConfiguration { type Value = ::Config; }

Robert Czechowski's avatar
Robert Czechowski committed
696
697
#[cfg(feature = "watch")]
pub fn get_handlebars_engine() -> impl AfterMiddleware {
698
    // HandlebarsEngine will look up all files with "./examples/templates/**/*.hbs"
Robert Czechowski's avatar
Robert Czechowski committed
699
700
701
702
703
704
705
706
707
708
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new("./templates/", ".hbs")));

    // load templates from all registered sources
    if let Err(r) = hbse.reload() {
        panic!("{}", r);
    }

    use std::sync::Arc;
    use handlebars_iron::Watchable;
709

Robert Czechowski's avatar
Robert Czechowski committed
710
711
712
713
714
715
716
    let hbse_ref = Arc::new(hbse);
    hbse_ref.watch("./templates/");
    hbse_ref
}

#[cfg(not(feature = "watch"))]
pub fn get_handlebars_engine() -> impl AfterMiddleware {
717
    // HandlebarsEngine will look up all files with "./examples/templates/**/*.hbs"
Robert Czechowski's avatar
Robert Czechowski committed
718
719
720
721
722
723
724
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new("./templates/", ".hbs")));

    // load templates from all registered sources
    if let Err(r) = hbse.reload() {
        panic!("{}", r);
    }
Robert Czechowski's avatar
Robert Czechowski committed
725
726

    hbse
Robert Czechowski's avatar
Robert Czechowski committed
727
728
}

729
fn cookie_warning(req: &mut Request) -> IronResult<Response> {
730
    match req.get_session_token() {
Daniel Brüning's avatar
Daniel Brüning committed
731
        Some(_session_token) => {
732
733
734
            // TODO: Set session!
            // TODO:
            Ok(Response::with((status::Found, RedirectRaw(format!("/{}",req.url.query().unwrap_or(""))))))
735
        },
736
737
738
739
740
741
        None => {
            let mut resp = Response::new();
            resp.set_mut(Template::new("cookie", json_val::Map::new())).set_mut(status::Ok);
            Ok(resp)
        }
    }
742
743


744
}
Robert Czechowski's avatar
Robert Czechowski committed
745

746
pub fn start_server(conn: Connection, config: ::Config) -> iron::error::HttpResult<iron::Listening> {
Robert Czechowski's avatar
Robert Czechowski committed
747
    let router = router!(
Robert Czechowski's avatar
Robert Czechowski committed
748
        greet: get "/" => greet_personal,
Robert Czechowski's avatar
Robert Czechowski committed
749
        contests: get "/contest/" => contests,
Robert Czechowski's avatar
Robert Czechowski committed
750
        contest: get "/contest/:contestid" => contest,
751
        contestresults: get "/contest/:contestid/result/" => contestresults,
Robert Czechowski's avatar
Robert Czechowski committed
752
        contest_post: post "/contest/:contestid" => contest_post,
Robert Czechowski's avatar
Robert Czechowski committed
753
754
        login: get "/login" => login,
        login_post: post "/login" => login_post,
Robert Czechowski's avatar
Robert Czechowski committed
755
756
        login_code_post: post "/clogin" => login_code_post,
        logout: get "/logout" => logout,
Robert Czechowski's avatar
Robert Czechowski committed
757
758
759
760
761
762
763
764
765
        subm: get "/submission/:taskid" => submission,
        subm_post: post "/submission/:taskid" => submission_post,
        subm_load: get "/load/:taskid" => submission,
        subm_save: post "/save/:taskid" => submission_post,
        groups: get "/group/" => groups,
        groups: post "/group/" => new_group,
        group: get "/group/:groupid" => group,
        group_post: post "/group" => group_post,
        profile: get "/profile" => profile,
Robert Czechowski's avatar
Robert Czechowski committed
766
767
        profile_post: post "/profile" => profile_post,
        user: get "/user/:userid" => user,
768
        user_post: post "/user/:userid" => user_post,
Robert Czechowski's avatar
Robert Czechowski committed
769
        task: get "/task/:taskid" => task,
770
        oauth: get "/oauth" => oauth,
771
        check_cookie: get "/cookie" => cookie_warning,
Robert Czechowski's avatar
Robert Czechowski committed
772
773
774
775
776
777
778
779
    );

    let my_secret = b"verysecret".to_vec();

    let mut mount = Mount::new();

    // Serve the shared JS/CSS at /
    mount.mount("/static/", Static::new(Path::new("static")));
Robert Czechowski's avatar
Robert Czechowski committed
780
    mount.mount("/tasks/", Static::new(Path::new(TASK_DIR)));
Robert Czechowski's avatar
Robert Czechowski committed
781
782
783
    mount.mount("/", router);

    let mut ch = Chain::new(mount);
784

Robert Czechowski's avatar
Robert Czechowski committed
785
    ch.link(Write::<SharedDatabaseConnection>::both(conn));
786
    ch.link(Write::<SharedConfiguration>::both(config.clone()));
787
    ch.link_around(CookieDistributor::new());
Robert Czechowski's avatar
Robert Czechowski committed
788
789
    ch.link_around(SessionStorage::new(SignedCookieBackend::new(my_secret)));

790

Robert Czechowski's avatar
Robert Czechowski committed
791
    ch.link_after(get_handlebars_engine());
Robert Czechowski's avatar
Robert Czechowski committed
792
    ch.link_after(ErrorReporter);
793

794
    let socket_addr = format!("{}:{}", config.host.unwrap(), config.port.unwrap());
795

796
    let srvr = Iron::new(ch).http(&socket_addr);
797
    println!("Listening on {}.", &socket_addr);
798
    srvr
Robert Czechowski's avatar
Robert Czechowski committed
799
}