webfw_iron.rs 50.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*  medal                                                                                                            *\
 *  Copyright (C) 2020  Bundesweite Informatikwettbewerbe                                                            *
 *                                                                                                                   *
 *  This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero        *
 *  General Public License as published  by the Free Software Foundation, either version 3 of the License, or (at    *
 *  your option) any later version.                                                                                  *
 *                                                                                                                   *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the       *
 *  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public      *
 *  License for more details.                                                                                        *
 *                                                                                                                   *
 *  You should have received a copy of the GNU Affero General Public License along with this program.  If not, see   *
\*  <http://www.gnu.org/licenses/>.                                                                                  */

Robert Czechowski's avatar
Robert Czechowski committed
15
16
use std::path::Path;

Robert Czechowski's avatar
Robert Czechowski committed
17
18
19
pub use handlebars_iron::handlebars::to_json;
use handlebars_iron::{DirectorySource, HandlebarsEngine, Template};
use iron;
Robert Czechowski's avatar
Robert Czechowski committed
20
use iron::modifiers::Redirect;
21
use iron::modifiers::RedirectRaw;
Robert Czechowski's avatar
Robert Czechowski committed
22
23
24
use iron::prelude::*;
use iron::{status, AfterMiddleware, AroundMiddleware, Handler};
use iron_sessionstorage;
Robert Czechowski's avatar
Robert Czechowski committed
25
use iron_sessionstorage::backends::SignedCookieBackend;
Robert Czechowski's avatar
Robert Czechowski committed
26
use iron_sessionstorage::traits::*;
Robert Czechowski's avatar
Robert Czechowski committed
27
use iron_sessionstorage::SessionStorage;
Robert Czechowski's avatar
Robert Czechowski committed
28
use mount::Mount;
29
use persistent::{Read, Write};
30
use reqwest;
Robert Czechowski's avatar
Robert Czechowski committed
31
32
33
use router::Router;
use staticfile::Static;
use urlencoded::{UrlEncodedBody, UrlEncodedQuery};
34

Robert Czechowski's avatar
Robert Czechowski committed
35
36
#[cfg(feature = "debug")]
use iron::BeforeMiddleware;
Robert Czechowski's avatar
Robert Czechowski committed
37

38
use config::Config;
39
use core;
Robert Czechowski's avatar
Robert Czechowski committed
40
41
42
use db_conn::MedalConnection;
use iron::typemap::Key;
pub use serde_json::value as json_val;
43

44
static TASK_DIR: &str = "tasks";
Robert Czechowski's avatar
Robert Czechowski committed
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

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)),* ]
        )
    );
}

60
macro_rules! with_conn {
61
    ( $x:expr , $c:ident, $r:expr , $($y:expr),* ) => {
62
        {
63
            let mutex = $r.get::<Write<SharedDatabaseConnection<$c>>>().unwrap();
64
65
66
67
68
            let conn = mutex.lock().unwrap_or_else(|e| e.into_inner());
            $x(&*conn, $($y),*)
        }
    };
}
Robert Czechowski's avatar
Robert Czechowski committed
69

70
macro_rules! template_ok {
Robert Czechowski's avatar
rustfmt    
Robert Czechowski committed
71
72
73
74
75
76
77
    ( $x:expr ) => {{
        let (template, data) = $x;

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

80
/** Show error messages on commandline */
Robert Czechowski's avatar
Robert Czechowski committed
81
82
83
84
85
86
87
88
struct ErrorReporter;
impl AfterMiddleware for ErrorReporter {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        println!("{}", err);
        Err(err)
    }
}

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/** Show error messages to users */
struct ErrorShower;
impl AfterMiddleware for ErrorShower {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        let IronError { error, response } = err;
        if response.body.is_none() {
            Ok(match response.status {
                Some(s) => {
                    let n = s.to_u16();
                    if n >= 400 && n <= 599 {
                        response.set((mime!(Text / Html),
                                      format!("<h1>{} {}</h1>", n, s.canonical_reason().unwrap_or("(Unknown error)"))))
                    } else {
                        response
                    }
                }
                _ => response,
            })
        } else {
            Err(IronError { error, response })
        }
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
113
#[derive(Debug)]
Robert Czechowski's avatar
Robert Czechowski committed
114
struct SessionToken {
Robert Czechowski's avatar
Robert Czechowski committed
115
    token: String,
Robert Czechowski's avatar
Robert Czechowski committed
116
117
118
119
120
121
122
123
124
125
126
127
128
}
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 })
        }
    }
}

129
130
131
pub struct CookieDistributor {}

impl AroundMiddleware for CookieDistributor {
132
    fn around(self, handler: Box<dyn Handler>) -> Box<dyn Handler> {
133
134
        use rand::{distributions::Alphanumeric, thread_rng, Rng};

135
        Box::new(move |req: &mut Request| -> IronResult<Response> {
136
            if req.session().get::<SessionToken>().expect("blub...").is_none() {
137
138
139
140
141
142
143
144
                let session_token: String = thread_rng().sample_iter(&Alphanumeric).take(10).collect();
                req.session().set(SessionToken { token: session_token }).unwrap();
            }
            handler.handle(req)
        })
    }
}

Robert Czechowski's avatar
Robert Czechowski committed
145
#[cfg(feature = "debug")]
146
147
pub struct RequestLogger {}

Robert Czechowski's avatar
Robert Czechowski committed
148
#[cfg(feature = "debug")]
149
150
151
152
153
154
155
156
impl BeforeMiddleware for RequestLogger {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        println!("{}: {}", req.method, req.url);

        Ok(())
    }
}

157
158
#[derive(Debug)]
struct SessionError {
Robert Czechowski's avatar
Robert Czechowski committed
159
    message: String,
160
161
}
impl ::std::error::Error for SessionError {
Robert Czechowski's avatar
Robert Czechowski committed
162
    fn description(&self) -> &str { &self.message }
163
164
165
}

impl ::std::fmt::Display for SessionError {
Robert Czechowski's avatar
Robert Czechowski committed
166
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "{}", self.message) }
167
168
}

169
170
171
172
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>;
173
174
}

175
176
impl<'a, 'b> RequestSession for Request<'a, 'b> {
    fn get_session_token(self: &mut Self) -> Option<String> {
177
        let session_token = self.session().get::<SessionToken>().unwrap();
Robert Czechowski's avatar
Robert Czechowski committed
178
        (|st: Option<SessionToken>| -> Option<String> { Some(st?.token) })(session_token)
179
180
181
    }

    fn require_session_token(self: &mut Self) -> IronResult<String> {
182
        match self.session().get::<SessionToken>().unwrap() {
183
184
            Some(SessionToken { token: session }) => Ok(session),
            _ => {
Robert Czechowski's avatar
Robert Czechowski committed
185
                use rand::{distributions::Alphanumeric, thread_rng, Rng};
186

Daniel Brüning's avatar
Daniel Brüning committed
187
                let new_session_key: String = thread_rng().sample_iter(&Alphanumeric).take(28).collect();
188
                self.session().set(SessionToken { token: new_session_key }).unwrap();
Robert Czechowski's avatar
rustfmt    
Robert Czechowski committed
189
190
191
192
193
194
195
196
197
                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("/"))),
                    )),
                })
198
199
200
201
202
            }
        }
    }

    fn expect_session_token(self: &mut Self) -> IronResult<String> {
203
        match self.session().get::<SessionToken>().unwrap() {
204
            Some(SessionToken { token: session }) => Ok(session),
Robert Czechowski's avatar
Robert Czechowski committed
205
206
207
            _ => Err(IronError { error: Box::new(SessionError { message:
                                                                    "No valid session found, access denied".to_string() }),
                                 response: Response::with(status::Forbidden) }),
208
209
210
211
212
213
214
215
        }
    }
}

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>;
216
    fn expect_str(self: &mut Self, key: &str) -> IronResult<String>;
217
218
219
220
221
222
223
224
225
226
}

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()?)
    }
227

228
229
230
    fn expect_int<T: ::std::str::FromStr>(self: &mut Self, key: &str) -> IronResult<T> {
        match self.get_int::<T>(key) {
            Some(i) => Ok(i),
Robert Czechowski's avatar
Robert Czechowski committed
231
232
233
            _ => Err(IronError { error: Box::new(SessionError { message:
                                                                    "No valid routing parameter".to_string() }),
                                 response: Response::with(status::Forbidden) }),
234
235
        }
    }
236

237
238
239
240
241
242
243
244
    fn expect_str(self: &mut Self, key: &str) -> IronResult<String> {
        match self.get_str(key) {
            Some(s) => Ok(s),
            _ => Err(IronError { error: Box::new(SessionError { message:
                                                                    "Routing parameter missing".to_string() }),
                                 response: Response::with(status::Forbidden) }),
        }
    }
245
246
}

247
struct AugMedalError<'c, 'a: 'c, 'b: 'c + 'a>(core::MedalError, &'c mut Request<'a, 'b>);
248
249
250
251

impl<'c, 'a, 'b> From<AugMedalError<'c, 'a, 'b>> for IronError {
    fn from(AugMedalError(me, req): AugMedalError<'c, 'a, 'b>) -> Self {
        match me {
252
            core::MedalError::NotLoggedIn => {
Robert Czechowski's avatar
Robert Czechowski committed
253
254
255
256
257
                IronError { error: Box::new(SessionError { message:
                                                               "Not Logged in, redirecting to login page".to_string() }),
                            response: Response::with((status::Found,
                                                      RedirectRaw(format!("/login?{}", req.url.path().join("/"))))) }
            }
258
259
260
261
262
263
264
            core::MedalError::AccessDenied => IronError { error: Box::new(SessionError { message:
                                                                                             "Access denied".to_string() }),
                                                          response: Response::with(status::Unauthorized) },
            core::MedalError::CsrfCheckFailed => IronError { error: Box::new(SessionError { message:
                                                                                                "CSRF Error".to_string() }),
                                                             response: Response::with(status::Forbidden) },
            core::MedalError::SessionTimeout => {
Robert Czechowski's avatar
Robert Czechowski committed
265
266
267
                IronError { error: Box::new(SessionError { message: "Session timed out".to_string() }),
                            response: Response::with(status::Forbidden) }
            }
268
            core::MedalError::DatabaseError => {
Robert Czechowski's avatar
Robert Czechowski committed
269
                IronError { error: Box::new(SessionError { message: "Database Error".to_string() }),
270
                            response: Response::with(status::InternalServerError) }
Robert Czechowski's avatar
Robert Czechowski committed
271
            }
272
            core::MedalError::PasswordHashingError => {
Robert Czechowski's avatar
Robert Czechowski committed
273
                IronError { error: Box::new(SessionError { message: "Error hashing the passwords".to_string() }),
274
                            response: Response::with(status::InternalServerError) }
Robert Czechowski's avatar
Robert Czechowski committed
275
            }
276
            core::MedalError::UnmatchedPasswords => {
Robert Czechowski's avatar
Robert Czechowski committed
277
278
279
280
                IronError { error: Box::new(SessionError { message:
                                                               "The two passwords did not match.".to_string() }),
                            response: Response::with(status::Forbidden) }
            }
281
        }
282
283
284
    }
}

285
286
287
trait RequestAugmentMedalError<'c, 'a: 'c, 'b: 'c + 'a, R> {
    fn aug(self, req: &'c mut Request<'a, 'b>) -> Result<R, AugMedalError<'c, 'a, 'b>>;
}
288
impl<'c, 'a: 'c, 'b: 'c + 'a, T> RequestAugmentMedalError<'c, 'a, 'b, T> for Result<T, core::MedalError> {
289
    fn aug(self, req: &'c mut Request<'a, 'b>) -> Result<T, AugMedalError<'c, 'a, 'b>> {
Robert Czechowski's avatar
Robert Czechowski committed
290
        self.map_err(move |me| AugMedalError(me, req))
291
292
293
    }
}

294
295
fn greet_personal<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
296
    let session_token = req.get_session_token();
Robert Czechowski's avatar
Robert Czechowski committed
297
298
    // hier ggf. Daten aus dem Request holen

299
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
300
    let oauth_infos = (config.self_url.clone(), config.oauth_providers.clone());
301

302
    let (template, mut data) = with_conn![core::index, C, req, session_token, oauth_infos];
303

304
305
306
307
308
    /*if let Some(server_message) = &config.server_message {
        data.insert("server_message".to_string(), to_json(&server_message));
    }*/
    config.server_message.as_ref().map(|sm| data.insert("server_message".to_string(), to_json(&sm)));

309
    // Antwort erstellen und zurücksenden
Robert Czechowski's avatar
Robert Czechowski committed
310
311
312
313
314
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

315
316
fn dbstatus<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
317
    let status = with_conn![core::status, C, req, ()];
318
319
320
321
322
323

    let mut resp = Response::new();
    resp.set_mut(status).set_mut(status::Ok);
    Ok(resp)
}

324
325
fn debug<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
326
327
    let session_token = req.get_session_token();

328
    let (template, data) = with_conn![core::debug, C, req, session_token];
329
330
331
332
333
334

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

335
336
fn debug_new_token<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
337
338
    let session_token = req.get_session_token();

339
340
    #[cfg(feature = "debug")]
    println!("Logging out session {:?}", session_token);
341

342
    with_conn![core::logout, C, req, session_token];
343
344
345
346

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

347
348
fn debug_logout<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
349
350
    let session_token = req.get_session_token();

351
352
    #[cfg(feature = "debug")]
    println!("Logging out session {:?}", session_token);
353

354
    with_conn![core::logout, C, req, session_token];
355
356
357
358

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

359
360
fn debug_create_session<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
361
362
    let session_token = req.get_session_token();

363
    with_conn![core::debug_create_session, C, req, session_token];
364
365
366
367

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

368
369
fn contests<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
370
    let session_token = req.require_session_token()?;
371
372
373
374
375
376
377
    let query_string = req.url.query().unwrap_or("").to_string();

    // TODO: Move to core::* ?
    let visibility = if query_string.contains("open") {
        core::ContestVisibility::Open
    } else if query_string.contains("current") {
        core::ContestVisibility::Current
378
379
    } else if query_string.contains("challenge") {
        core::ContestVisibility::LoginRequired
380
381
382
    } else {
        core::ContestVisibility::All
    };
383

384
385
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
    let (self_url, oauth_providers) = (config.self_url.clone(), config.oauth_providers.clone());
386

387
388
    let (template, mut data) =
        with_conn![core::show_contests, C, req, &session_token, (self_url, oauth_providers), visibility];
389
390

    config.server_message.as_ref().map(|sm| data.insert("server_message".to_string(), to_json(&sm)));
391

392
393
394
395
    if query_string.contains("results") {
        data.insert("direct_link_to_results".to_string(), to_json(&true));
    }

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

401
402
fn contest<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
403
    let contest_id = req.expect_int::<i32>("contestid")?;
404
    let session_token = req.require_session_token()?;
405
    let query_string = req.url.query().map(|s| s.to_string());
Robert Czechowski's avatar
Robert Czechowski committed
406

407
408
409
410
411
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
    let oauth_infos = (config.self_url.clone(), config.oauth_providers.clone());

    let (template, data) =
        with_conn![core::show_contest, C, req, contest_id, &session_token, query_string, oauth_infos].aug(req)?;
Robert Czechowski's avatar
Robert Czechowski committed
412
413
414
415
416
417

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

418
419
fn contestresults<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
420
421
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
    let disable_contest_results = config.disable_results_page.unwrap_or(false);
422
423
424
425
426
427
428

    if disable_contest_results {
        let mut resp = Response::new();
        resp.set_mut(Template::new(&"nocontestresults", 2)).set_mut(status::Locked);
        return Ok(resp);
    }

429
    let contest_id = req.expect_int::<i32>("contestid")?;
430
431
    let session_token = req.require_session_token()?;

432
    let (template, data) = with_conn![core::show_contest_results, C, req, contest_id, &session_token].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
433

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

439
440
441
442
443
444
fn contestresults_download<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
    let disable_contest_results = config.disable_results_page.unwrap_or(false);

    println!("test");
445

446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
    if disable_contest_results {
        let mut resp = Response::new();
        resp.set_mut(Template::new(&"nocontestresults", 2)).set_mut(status::Locked);
        return Ok(resp);
    }

    let contest_id = req.expect_int::<i32>("contestid")?;
    let session_token = req.require_session_token()?;

    let (template, data) = with_conn![core::show_contest_results, C, req, contest_id, &session_token].aug(req)?;

    use iron::headers::{Charset, ContentDisposition, DispositionParam, DispositionType};

    let cd = ContentDisposition { disposition: DispositionType::Attachment,
                                  parameters: vec![DispositionParam::Filename(
        Charset::Ext("Utf-8".to_string()), // The character set for the bytes of the filename
        None,                              // The optional language tag (see `language-tag` crate)
        format!("{}.csv", data.get("contestname").unwrap().as_str().unwrap()).as_bytes().to_vec(), // the actual bytes of the filename
464
                                                                                                   // TODO: The name should be returned by core::show_contest_results directly
465
466
467
468
469
470
471
472
    )] };

    let mut resp = Response::new();
    resp.headers.set(cd);
    resp.set_mut(Template::new(&format!("{}_download", template), data)).set_mut(status::Ok);
    Ok(resp)
}

473
474
fn contest_post<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
475
    let contest_id = req.expect_int::<i32>("contestid")?;
476
    let session_token = req.expect_session_token()?;
477

Robert Czechowski's avatar
Robert Czechowski committed
478
479
    let csrf_token = {
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
480
        iexpect!(formdata.get("csrf_token"))[0].to_owned()
Robert Czechowski's avatar
Robert Czechowski committed
481
482
    };

483
    // TODO: Was mit dem Result?
484
    with_conn![core::start_contest, C, req, contest_id, &session_token, &csrf_token].aug(req)?;
Robert Czechowski's avatar
Robert Czechowski committed
485

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

489
490
fn login<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
491
492
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
    let (self_url, oauth_providers) = (config.self_url.clone(), config.oauth_providers.clone());
493

494
    let mut data = json_val::Map::new();
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509

    let query_string = req.url.query().map(|s| s.to_string());
    if let Some(query) = query_string {
        data.insert("forward".to_string(), to_json(&query));
    }

    let mut oauth_links: Vec<(String, String, String)> = Vec::new();
    if let Some(oauth_providers) = oauth_providers {
        for oauth_provider in oauth_providers {
            oauth_links.push((oauth_provider.provider_id.to_owned(),
                              oauth_provider.login_link_text.to_owned(),
                              oauth_provider.url.to_owned()));
        }
    }

510
    data.insert("self_url".to_string(), to_json(&self_url));
511
    data.insert("oauth_links".to_string(), to_json(&oauth_links));
512
    data.insert("parent".to_string(), to_json(&"base"));
513

Robert Czechowski's avatar
Robert Czechowski committed
514
    let mut resp = Response::new();
515
    resp.set_mut(Template::new("login", data)).set_mut(status::Ok);
Robert Czechowski's avatar
Robert Czechowski committed
516
517
518
    Ok(resp)
}

519
520
fn login_post<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
Robert Czechowski's avatar
Robert Czechowski committed
521
522
    let logindata = {
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
Robert Czechowski's avatar
Robert Czechowski committed
523
        (iexpect!(formdata.get("username"))[0].to_owned(), iexpect!(formdata.get("password"))[0].to_owned())
Robert Czechowski's avatar
Robert Czechowski committed
524
525
    };

526
527
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
    let (self_url, oauth_providers) = (config.self_url.clone(), config.oauth_providers.clone());
528

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

531
    let loginresult = with_conn![core::login, C, req, logindata, (self_url, oauth_providers)];
Robert Czechowski's avatar
Robert Czechowski committed
532
533
534
535
536
537

    match loginresult {
        // Login successful
        Ok(sessionkey) => {
            req.session().set(SessionToken { token: sessionkey }).unwrap();
            Ok(Response::with((status::Found, Redirect(url_for!(req, "greet")))))
Robert Czechowski's avatar
Robert Czechowski committed
538
        }
Robert Czechowski's avatar
Robert Czechowski committed
539
540
541
542
543
544
545
546
547
        // Login failed
        Err((template, data)) => {
            let mut resp = Response::new();
            resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
            Ok(resp)
        }
    }
}

548
549
fn login_code_post<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
Robert Czechowski's avatar
Robert Czechowski committed
550
551
552
553
554
    let code = {
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
        iexpect!(formdata.get("code"))[0].to_owned()
    };

555
556
    let config = req.get::<Read<SharedConfiguration>>().unwrap();
    let (self_url, oauth_providers) = (config.self_url.clone(), config.oauth_providers.clone());
557

558
559
    // TODO: Submit current session to login

560
    let loginresult = with_conn![core::login_with_code, C, req, &code, (self_url, oauth_providers)];
Robert Czechowski's avatar
Robert Czechowski committed
561
562
563
564
565
566

    match loginresult {
        // Login successful
        Ok(Ok(sessionkey)) => {
            req.session().set(SessionToken { token: sessionkey }).unwrap();
            Ok(Response::with((status::Found, Redirect(url_for!(req, "greet")))))
Robert Czechowski's avatar
Robert Czechowski committed
567
        }
Robert Czechowski's avatar
Robert Czechowski committed
568
569
        Ok(Err(sessionkey)) => {
            req.session().set(SessionToken { token: sessionkey }).unwrap();
570
571
            //Ok(Response::with((status::Found, Redirect(url_for!(req, "profile")))))
            Ok(Response::with((status::Found,
572
573
                               Redirect(iron::Url::parse(&format!("{}?status=firstlogin",
                                                                  &url_for!(req, "profile"))).unwrap()))))
Robert Czechowski's avatar
Robert Czechowski committed
574
        }
Robert Czechowski's avatar
Robert Czechowski committed
575
576
577
578
579
580
581
        // Login failed
        Err((template, data)) => {
            let mut resp = Response::new();
            resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
            Ok(resp)
        }
    }
582
}
Robert Czechowski's avatar
Robert Czechowski committed
583

584
585
fn logout<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
586
    let session_token = req.get_session_token();
Robert Czechowski's avatar
Robert Czechowski committed
587

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

590
    with_conn![core::logout, C, req, session_token];
Robert Czechowski's avatar
Robert Czechowski committed
591
592
593
594

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

595
596
fn submission<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
597
    let task_id = req.expect_int::<i32>("taskid")?;
598
    let session_token = req.expect_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
599
600
601
    let subtask: Option<String> = (|| -> Option<String> {
        req.get_ref::<UrlEncodedQuery>().ok()?.get("subtask")?.get(0).map(|x| x.to_owned())
    })();
602

603
    let result = with_conn![core::load_submission, C, req, task_id, &session_token, subtask];
Robert Czechowski's avatar
Robert Czechowski committed
604
605

    match result {
Robert Czechowski's avatar
Robert Czechowski committed
606
607
        Ok(data) => Ok(Response::with((status::Ok, mime!(Application / Json), data.to_string()))),
        Err(_) => Ok(Response::with((status::BadRequest, mime!(Application / Json), "{}".to_string()))),
Robert Czechowski's avatar
Robert Czechowski committed
608
609
610
    }
}

611
612
fn submission_post<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
613
    let task_id = req.expect_int::<i32>("taskid")?;
614
    let session_token = req.expect_session_token()?;
615
    let (csrf_token, data, grade, subtask) = {
Robert Czechowski's avatar
Robert Czechowski committed
616
        let formdata = iexpect!(req.get_ref::<UrlEncodedBody>().ok());
617
618
619
620
        (iexpect!(formdata.get("csrf_token"))[0].to_owned(),
         iexpect!(formdata.get("data"))[0].to_owned(),
         iexpect!(formdata.get("grade").unwrap_or(&vec!["0".to_owned()])[0].parse::<i32>().ok()),
         formdata.get("subtask").map(|x| x[0].to_owned()))
Robert Czechowski's avatar
Robert Czechowski committed
621
    };
622
623
624

    #[cfg(feature = "debug")]
    println!("New submission for task {} (graded {}): {}", task_id, grade, data);
625

626
    let result =
627
        with_conn![core::save_submission, C, req, task_id, &session_token, &csrf_token, data, grade, subtask].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
628

629
    Ok(Response::with((status::Ok, mime!(Application / Json), result)))
Robert Czechowski's avatar
Robert Czechowski committed
630
631
}

632
633
fn task<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
634
    let task_id = req.expect_int::<i32>("taskid")?;
635
    let session_token = req.require_session_token()?;
636

637
    let (template, data) = with_conn![core::show_task, C, req, task_id, &session_token].aug(req)?;
Robert Czechowski's avatar
Robert Czechowski committed
638
639
640
641
642
643

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

644
645
fn groups<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
646
    let session_token = req.require_session_token()?;
Daniel Brüning's avatar
Daniel Brüning committed
647

648
    let (template, data) = with_conn![core::show_groups, C, req, &session_token].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
649

Robert Czechowski's avatar
Robert Czechowski committed
650
651
652
653
654
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

655
656
fn group<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
657
    let group_id = req.expect_int::<i32>("groupid")?;
658
    let session_token = req.require_session_token()?;
Daniel Brüning's avatar
Daniel Brüning committed
659

660
    let (template, data) = with_conn![core::show_group, C, req, group_id, &session_token].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
661

662
663
664
    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
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
fn group_download<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let group_id = req.expect_int::<i32>("groupid")?;
    let session_token = req.require_session_token()?;

    let (template, data) = with_conn![core::show_group, C, req, group_id, &session_token].aug(req)?;

    use iron::headers::{Charset, ContentDisposition, DispositionParam, DispositionType};

    let cd = ContentDisposition { disposition: DispositionType::Attachment,
                                  parameters: vec![DispositionParam::Filename(
        Charset::Ext("Utf-8".to_string()), // The character set for the bytes of the filename
        None,                              // The optional language tag (see `language-tag` crate)
        format!("{}.csv", data.get("groupname").unwrap().as_str().unwrap()).as_bytes().to_vec(), // the actual bytes of the filename
                                                                                                 // TODO: The name should be returned by core::show_group directly
    )] };

    let mut resp = Response::new();
    resp.headers.set(cd);
    resp.set_mut(Template::new(&format!("{}_download", template), data)).set_mut(status::Ok);
    Ok(resp)
}

690
//TODO: Secure with CSRF-Token?
691
692
fn group_post<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
693
    let group_id = req.expect_int::<i32>("groupid")?;
694
    let session_token = req.expect_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
695

696
    //TODO: use result?
697
    with_conn![core::modify_group, C, req, group_id, &session_token].aug(req)?;
Robert Czechowski's avatar
rustfmt    
Robert Czechowski committed
698

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

702
703
fn new_group<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
704
    let session_token = req.require_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
705

706
    let (csrf_token, name, tag) = {
Robert Czechowski's avatar
Robert Czechowski committed
707
        let formdata = iexpect!(req.get_ref::<UrlEncodedBody>().ok());
708
709
710
        (iexpect!(formdata.get("csrf_token"))[0].to_owned(),
         iexpect!(formdata.get("name"))[0].to_owned(),
         iexpect!(formdata.get("tag"))[0].to_owned())
Robert Czechowski's avatar
Robert Czechowski committed
711
712
    };

713
    let group_id = with_conn![core::add_group, C, req, &session_token, &csrf_token, name, tag].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
714

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

718
719
720
721
722
723
724
725
726
727
728
729
730
fn group_csv<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let session_token = req.require_session_token()?;

    template_ok!(with_conn![core::group_csv, C, req, &session_token].aug(req)?)
}

fn group_csv_upload<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let session_token = req.require_session_token()?;

    let (csrf_token, group_data) = {
        let formdata = iexpect!(req.get_ref::<UrlEncodedBody>().ok());
Robert Czechowski's avatar
rustfmt    
Robert Czechowski committed
731
        (iexpect!(formdata.get("csrf_token"))[0].to_owned(), iexpect!(formdata.get("group_data"))[0].to_owned())
732
733
    };

Robert Czechowski's avatar
rustfmt    
Robert Czechowski committed
734
735
    println!("{}", group_data);

736
    with_conn![core::upload_groups, C, req, &session_token, &csrf_token, &group_data].aug(req)?;
737
738
739
740

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

741
742
fn profile<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
743
    let session_token = req.require_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
744
    let query_string = req.url.query().map(|s| s.to_string());
Daniel Brüning's avatar
Daniel Brüning committed
745

746
    let (template, data) = with_conn![core::show_profile, C, req, &session_token, None, query_string].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
747

Robert Czechowski's avatar
Robert Czechowski committed
748
749
750
751
752
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

753
754
fn profile_post<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
755
    let session_token = req.expect_session_token()?;
756
    let (csrf_token, firstname, lastname, street, zip, city, pwd, pwd_repeat, grade, sex) = {
Robert Czechowski's avatar
Robert Czechowski committed
757
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
758
        (iexpect!(formdata.get("csrf_token"))[0].to_owned(),
Robert Czechowski's avatar
Robert Czechowski committed
759
760
         iexpect!(formdata.get("firstname"))[0].to_owned(),
         iexpect!(formdata.get("lastname"))[0].to_owned(),
761
762
763
764
765
         formdata.get("street").map(|x| x[0].to_owned()),
         formdata.get("zip").map(|x| x[0].to_owned()),
         formdata.get("city").map(|x| x[0].to_owned()),
         formdata.get("password").map(|x| x[0].to_owned()),
         formdata.get("password_repeat").map(|x| x[0].to_owned()),
766
767
         iexpect!(formdata.get("grade"))[0].parse::<i32>().unwrap_or(0),
         iexpect!(formdata.get("sex"))[0].parse::<i32>().ok())
Robert Czechowski's avatar
Robert Czechowski committed
768
    };
Daniel Brüning's avatar
Daniel Brüning committed
769

770
771
772
773
774
775
776
777
    let profilechangeresult =
        with_conn![core::edit_profile,
                   C,
                   req,
                   &session_token,
                   None,
                   &csrf_token,
                   (firstname, lastname, street, zip, city, pwd, pwd_repeat, grade, sex)].aug(req)?;
778
779
780
781
782

    Ok(Response::with((status::Found,
                       Redirect(iron::Url::parse(&format!("{}?status={:?}",
                                                          &url_for!(req, "profile"),
                                                          profilechangeresult)).unwrap()))))
Robert Czechowski's avatar
Robert Czechowski committed
783
784
}

785
786
fn user<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
787
    let user_id = req.expect_int::<i32>("userid")?;
788
    let session_token = req.expect_session_token()?;
Robert Czechowski's avatar
Robert Czechowski committed
789
    let query_string = req.url.query().map(|s| s.to_string());
Daniel Brüning's avatar
Daniel Brüning committed
790

Robert Czechowski's avatar
Robert Czechowski committed
791
    let (template, data) =
792
        with_conn![core::show_profile, C, req, &session_token, Some(user_id), query_string].aug(req)?;
Daniel Brüning's avatar
Daniel Brüning committed
793

Robert Czechowski's avatar
Robert Czechowski committed
794
795
796
797
798
    let mut resp = Response::new();
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
    Ok(resp)
}

799
800
fn user_post<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
801
    let user_id = req.expect_int::<i32>("userid")?;
802
    let session_token = req.expect_session_token()?;
803
    let (csrf_token, firstname, lastname, street, zip, city, pwd, pwd_repeat, grade, sex) = {
804
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
805
        (iexpect!(formdata.get("csrf_token"))[0].to_owned(),
Robert Czechowski's avatar
Robert Czechowski committed
806
807
         iexpect!(formdata.get("firstname"))[0].to_owned(),
         iexpect!(formdata.get("lastname"))[0].to_owned(),
808
809
810
811
812
         formdata.get("street").map(|x| x[0].to_owned()),
         formdata.get("zip").map(|x| x[0].to_owned()),
         formdata.get("city").map(|x| x[0].to_owned()),
         formdata.get("password").map(|x| x[0].to_owned()),
         formdata.get("password_repeat").map(|x| x[0].to_owned()),
813
814
         iexpect!(formdata.get("grade"))[0].parse::<i32>().unwrap_or(0),
         iexpect!(formdata.get("sex"))[0].parse::<i32>().ok())
815
    };
Daniel Brüning's avatar
Daniel Brüning committed
816

817
818
819
820
821
822
823
824
    let profilechangeresult =
        with_conn![core::edit_profile,
                   C,
                   req,
                   &session_token,
                   Some(user_id),
                   &csrf_token,
                   (firstname, lastname, street, zip, city, pwd, pwd_repeat, grade, sex)].aug(req)?;
825
826
827
828
829
830

    Ok(Response::with((status::Found,
                       Redirect(iron::Url::parse(&format!("{}?status={:?}",
                                                          &url_for!(req, "user", "userid" => format!("{}",user_id)),
                                                          profilechangeresult)).unwrap()))))
    //old:   Ok(Response::with((status::Found, Redirect(url_for!(req, "user", "userid" => format!("{}",user_id))))))
831
832
}

833
834
835
836
fn teacherinfos<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let session_token = req.expect_session_token()?;

837
838
    let config = req.get::<Read<SharedConfiguration>>().unwrap();

839
840
    let (template, data) =
        with_conn![core::teacher_infos, C, req, &session_token, config.teacher_page.as_ref().map(|x| &**x)].aug(req)?;
841
    // .as_ref().map(|x| &**x) can be written as .as_deref() since rust 1.40
842
843
844
845
846
847

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

848
fn admin<C>(req: &mut Request) -> IronResult<Response>
849
    where C: MedalConnection + std::marker::Send + 'static {
850
851
    let session_token = req.expect_session_token()?;

852
    let (template, data) = with_conn![core::admin_index, C, req, &session_token].aug(req)?;
853
854

    let mut resp = Response::new();
855
    resp.set_mut(Template::new(&template, data)).set_mut(status::Ok);
856
857
858
859
860
861
862
    Ok(resp)
}

fn admin_users<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let session_token = req.expect_session_token()?;

863
    let (s_id, s_firstname, s_lastname, s_logincode, s_groupcode, s_pms_id) = {
864
865
866
867
868
        let formdata = itry!(req.get_ref::<UrlEncodedBody>());
        (formdata.get("id").map(|x| x[0].parse::<i32>().unwrap_or(0)),
         formdata.get("firstname").map(|x| x[0].to_owned()),
         formdata.get("lastname").map(|x| x[0].to_owned()),
         formdata.get("logincode").map(|x| x[0].to_owned()),
869
         formdata.get("groupcode").map(|x| x[0].to_owned()),
870
871
872
873
874
875
876
         formdata.get("pmsid").map(|x| x[0].to_owned()))
    };

    let (template, data) = with_conn![core::admin_search_users,
                                      C,
                                      req,
                                      &session_token,
877
                                      (s_id, s_firstname, s_lastname, s_logincode, s_groupcode, s_pms_id)].aug(req)?;
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908

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

fn admin_user<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let user_id = req.expect_int::<i32>("userid")?;
    let session_token = req.expect_session_token()?;

    let csrf_token = if let Ok(formdata) = req.get_ref::<UrlEncodedBody>() {
        // or iexpect!(formdata.get("csrf_token"))[0].to_owned(), ?
        formdata.get("csrf_token").map(|x| x[0].to_owned())
    } else {
        None
    };

    let (template, data) = if let Some(csrf_token) = csrf_token {
        with_conn![core::admin_delete_user, C, req, user_id, &session_token, &csrf_token].aug(req)?
    } else {
        with_conn![core::admin_show_user, C, req, user_id, &session_token].aug(req)?
    };

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

fn admin_group<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
909
    let group_id = req.expect_int::<i32>("groupid")?;
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
    let session_token = req.expect_session_token()?;

    let csrf_token = if let Ok(formdata) = req.get_ref::<UrlEncodedBody>() {
        formdata.get("csrf_token").map(|x| x[0].to_owned())
    } else {
        None
    };

    let (template, data) = if let Some(csrf_token) = csrf_token {
        with_conn![core::admin_delete_group, C, req, group_id, &session_token, &csrf_token].aug(req)?
    } else {
        with_conn![core::admin_show_group, C, req, group_id, &session_token].aug(req)?
    };

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

fn admin_participation<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
931
932
    let user_id = req.expect_int::<i32>("userid")?;
    let contest_id = req.expect_int::<i32>("contestid")?;
933
934
935
936
937
938
939
940
941
    let session_token = req.expect_session_token()?;

    let csrf_token = if let Ok(formdata) = req.get_ref::<UrlEncodedBody>() {
        formdata.get("csrf_token").map(|x| x[0].to_owned())
    } else {
        None
    };

    let (template, data) = if let Some(csrf_token) = csrf_token {
942
        with_conn![core::admin_delete_participation, C, req, user_id, contest_id, &session_token, &csrf_token].aug(req)?
943
    } else {
944
        with_conn![core::admin_show_participation, C, req, user_id, contest_id, &session_token].aug(req)?
945
946
947
948
949
950
951
    };

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

952
953
954
955
956
957
958
959
960
961
962
fn admin_contests<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let session_token = req.expect_session_token()?;

    let (template, data) = with_conn![core::admin_show_contests, C, req, &session_token].aug(req)?;

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

963
964
965
966
967
968
969
970
971
972
fn admin_export_contest<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
    let contest_id = req.expect_int::<i32>("contestid")?;
    let session_token = req.expect_session_token()?;

    let filename = with_conn![core::admin_contest_export, C, req, contest_id, &session_token].aug(req)?;

    Ok(Response::with((status::Found, RedirectRaw(format!("/export/{}", filename)))))
}

973
974
#[derive(Deserialize, Debug)]
struct OAuthAccess {
Robert Czechowski's avatar
Robert Czechowski committed
975
976
    access_token: String,
    token_type: String,
977
    refresh_token: String,
978
979
    expires: Option<i32>,    // documented as 'expires_in'
    expires_in: Option<i32>, // sent as 'expires'
980
981
982
}

#[derive(Deserialize, Debug)]
983
#[allow(non_snake_case)]
984
pub struct OAuthUserData {
Robert Czechowski's avatar
Robert Czechowski committed
985
986
987
988
989
990
    userID: Option<String>, // documented as 'userId'
    userId: Option<String>, // sent as 'userID'
    userType: String,
    gender: String,
    firstName: String,
    lastName: String,
991
    dateOfBirth: Option<String>,
Robert Czechowski's avatar
Robert Czechowski committed
992
    eMail: Option<String>,
993
    userId_int: Option<String>,
994
995
}

996
// TODO: Most of this code should be moved into core:: as a new function
997
998
fn oauth<C>(req: &mut Request) -> IronResult<Response>
    where C: MedalConnection + std::marker::Send + 'static {
999
1000
    use params::{Params, Value};

1001
    let oauth_id = req.expect_str("oauthid")?;
1002

1003
    let (client_id, client_secret, access_token_url, user_data_url) = {
1004
        let config = req.get::<Read<SharedConfiguration>>().unwrap();
1005
1006

        let mut result: Option<(String, String, String, String)> = None;
1007

Robert Czechowski's avatar