Skip to main content

medal/
db_conn.rs

1/*  medal                                                                                                            *\
2 *  Copyright (C) 2022  Bundesweite Informatikwettbewerbe, Robert Czechowski                                                            *
3 *                                                                                                                   *
4 *  This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero        *
5 *  General Public License as published  by the Free Software Foundation, either version 3 of the License, or (at    *
6 *  your option) any later version.                                                                                  *
7 *                                                                                                                   *
8 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the       *
9 *  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public      *
10 *  License for more details.                                                                                        *
11 *                                                                                                                   *
12 *  You should have received a copy of the GNU Affero General Public License along with this program.  If not, see   *
13\*  <http://www.gnu.org/licenses/>.                                                                                  */
14
15use config;
16use db_objects::*;
17
18#[derive(Debug)]
19pub enum SignupResult {
20    SignedUp,
21    EmailTaken,
22    UsernameTaken,
23    UserLoggedIn,
24    EmptyFields,
25}
26
27/// This trait abstracts the database connection and provides function for all actions to be performed on the database
28/// in the medal platform.
29pub trait MedalConnection {
30    fn reconnect(config: &config::Config) -> Self;
31
32    fn dbtype(&self) -> &'static str;
33
34    fn migration_already_applied(&self, name: &str) -> bool;
35    fn apply_migration(&mut self, name: &str, contents: &str);
36
37    fn code_exists(&self, code: &str) -> bool;
38
39    /// Try to get session associated to the session token `key`.
40    ///
41    /// Returns an `Option` that can contain the `SessionUser` of the session if the session exists and is not expired or
42    /// `None` otherwise.
43    fn get_session(&self, key: &str) -> Option<SessionUser>;
44
45    /// Create a new anonymous session with the session token `key`.
46    ///
47    /// Returns the `SessionUser` of the session.
48    fn new_session(&self, key: &str) -> SessionUser;
49    /// Set activity date (for testing purposes)
50    fn session_set_activity_dates(&self, session_id: i64, account_created: Option<time::Timespec>,
51                                  last_login: Option<time::Timespec>, last_activity: Option<time::Timespec>);
52    /// Saves the session data of `session` in the database.
53    fn save_session(&self, session: SessionUser);
54    /// Combination of [`get_session`](#tymethod.get_session) and  [`new_session`](#tymethod.new_session).
55    ///
56    /// This method can still fail in case of database error in order to bubble them up to the webframework
57    fn get_session_or_new(&self, key: &str) -> Result<SessionUser, ()>;
58
59    fn get_team_partners_by_contest_and_teamlead(&self, contest_id: i64, teamlead_id: i64) -> Vec<SessionUser>;
60
61    /// Try to get session associated to the id `user_id`.
62    ///
63    /// Returns an `Option` that can contain the `SessionUser` of the session if the session exists or `None` otherwise.
64    fn get_user_by_id(&self, user_id: i64) -> Option<SessionUser>;
65
66    /// Try to get session and user group associated to the id `user_id`.
67    ///
68    /// Returns an `Option` that can contain a pair of `SessionUser` and `Option<Group>` of the session and optionally
69    /// the group if the session exists or `None` otherwise.
70    fn get_user_and_group_by_id(&self, user_id: i64) -> Option<(SessionUser, Option<Group>)>;
71    fn get_user_and_group_by_logincode(&self, logincode: &str) -> Option<(SessionUser, Option<Group>)>;
72
73    fn set_webauthn_passkey_registration(&self, user_id: i64, passkey_registration: &str);
74    fn get_webauthn_passkey_registration(&self, user_id: i64) -> Option<String>;
75    fn check_webauthn_cred_id_in_use(&self, cred_id: &str) -> bool;
76    fn add_webauthn_passkey(&self, user_id: i64, cred_id: &str, passkey: &str, name: &str) -> i64;
77    fn delete_webauthn_passkey(&self, session_id: i64, passkey_id: i64);
78
79    fn get_webauthn_passkey_names_for_user(&self, user_id: i64) -> Vec<(i64, String)>;
80    fn get_all_webauthn_passkeys(&self) -> Vec<String>;
81    fn get_all_webauthn_credentials(&self) -> Vec<String>;
82    fn store_webauthn_auth_challenge(&self, challenge: &str, authentication: &str) -> i64;
83    fn get_webauthn_auth_challenge_by_id(&self, auth_id: i64) -> Option<String>;
84
85    /// Try to login in the user with `username` and `password`.
86    ///
87    /// Returns a `Result` that either contains the new session token for the user if the login was successfull or no
88    /// value if the login was not successfull.
89    fn update_grade(&self, session_token: &str, timediff: i32) -> bool;
90    fn login(&self, session: Option<&str>, username: &str, password: &str) -> Result<(String, i32), ()>;
91    fn login_with_code(&self, session: Option<&str>, logincode: &str) -> Result<(String, i32), ()>;
92    fn login_with_key(&self, session: Option<&str>, cred_id: &str) -> Result<(String, i32), ()>;
93    fn login_foreign(&self, session: Option<&str>, provider_id: &str, foreign_id: &str,
94                     _: (bool, bool, &str, &str, Option<i32>, &Option<String>))
95                     -> Result<(String, i32, Option<time::Timespec>), ()>;
96    fn create_user_with_groupcode(&self, session: Option<&str>, groupcode: &str) -> Result<String, ()>;
97    fn update_or_create_group_with_users(&self, group: Group, admin: i64);
98    fn add_admin_to_group(&self, group: &mut Group, admin: i64);
99    fn remove_admin_from_group(&self, group: &mut Group, admin: i64);
100
101    /// Logs out the user identified by session token `session` by resetting the uesr's session token in the database
102    /// to `NULL`.
103    fn logout(&self, session: &str);
104
105    fn signup(&self, session_token: &str, username: &str, email: &str, password_hash: String, salt: &str)
106              -> SignupResult;
107
108    fn load_submission(&self, session: &SessionUser, task_id: i64, subtask: Option<&str>) -> Option<Submission>;
109    fn get_all_submissions(&self, session_id: i64, task_id: i64, subtask: Option<&str>) -> Vec<Submission>;
110    fn submit_submission(&self, submission: Submission);
111    fn get_grade_by_user_and_task(&self, session_id: i64, task_id: i64) -> Grade;
112    fn get_contest_groups_grades(&self, session_id: i64, contest_id: i64)
113                                 -> (Vec<String>, Vec<(Group, Vec<(UserInfo, Vec<Grade>)>)>);
114    fn get_taskgroup_user_grade(&self, session_id: i64, taskgroup_id: i64) -> Grade;
115    fn get_contest_user_grades(&self, session_id: i64, contest_id: i64) -> Vec<Grade>;
116    fn export_contest_results_to_file(&self, contest_id: i64, taskgroups_ids: &[(i64, String)], filename: &str);
117
118    fn insert_contest_annotations(&self, contest_id: i64, annotations: Vec<(i64, Option<String>)>) -> i64;
119
120    /// Returns the submission identified by `submission_id`, together with its grade, task, taskgroup and contest.
121    fn get_submission_by_id_complete_shallow_contest(&self, submission_id: i64)
122                                                     -> Option<(Submission, Task, Taskgroup, Contest)>;
123
124    /// Returns a `Vec` of /all/ contests ever defined.
125    fn get_contest_list(&self) -> Vec<Contest>;
126
127    /// Returns a `Vec` of contests where participations exist.
128    fn get_contest_list_with_group_member_participations(&self, session_id: i64) -> Vec<Contest>;
129
130    /// Returns the contest identified by `contest_id` without any associated taskgroups. Panics if the contest does not
131    /// exist.
132    fn get_contest_by_id(&self, contest_id: i64) -> Option<Contest>;
133
134    /// Returns the contest identified by `contest_id` with associated taskgroups but without any associated tasks of
135    /// the taskgroups. Panics if the contest does not exist.
136    fn get_contest_by_id_partial(&self, contest_id: i64) -> Option<Contest>;
137
138    /// Returns the contest identified by `contest_id` with associated taskgroups and all associated tasks of the
139    /// taskgroups. Panics if the contest does not exist.
140    fn get_contest_by_id_complete(&self, contest_id: i64) -> Option<Contest>;
141
142    /// Try to get the participation associated to the session id `session_id` and the contest id `contest_id`.
143    ///
144    /// Returns an `Option` that can contain the `Participation` if it exists or `None` otherwise.
145    fn get_participation(&self, session_id: i64, contest_id: i64) -> Option<Participation>;
146
147    /// Try to get the participation associated to the session token `session` and the contest id `contest_id`.
148    ///
149    /// Returns an `Option` that can contain the `Participation` if it exists or `None` otherwise.
150    fn get_own_participation(&self, session_id: i64, contest_id: i64) -> Option<Participation>;
151
152    /// Collect all the participation associated to the session token `session`.
153    ///
154    /// Returns an `Vec` that contains pairs of all participations with their associated contests.
155    fn get_all_participations_complete(&self, session_id: i64) -> Vec<(Participation, Contest)>;
156
157    fn count_all_stars(&self, session_id: i64) -> i32;
158    fn count_all_stars_by_contest(&self, session_id: i64) -> Vec<(i64, i32)>;
159
160    fn has_participation_by_contest_file(&self, session_id: i64, location: &str, filename: &str) -> bool;
161    fn has_participation_by_contest_file_and_stars(&self, session_id: i64, location: &str, filename: &str, stars: i32)
162                                                   -> bool;
163
164    /// Start a new participation of the session identified by the session token `session` for the contest with the
165    /// contest id `contest_id`. It checks whether the session is allowed to start the participation.
166    ///
167    /// Returns an `Result` that either contains the new `Participation` if the checks succeded or no value if the
168    /// checks failed.
169    fn new_participation(&self, session_id: i64, contest_id: i64, team: Option<i64>) -> Result<Participation, ()>;
170    fn get_task_by_id(&self, task_id: i64) -> Option<Task>;
171    fn get_task_by_id_complete(&self, task_id: i64) -> Option<(Task, Taskgroup, Contest)>;
172
173    fn get_submission_to_validate(&self, tasklocation: &str, subtask: Option<&str>) -> i64;
174    fn find_next_submission_to_validate(&self, user_id: i64, taskgroup_id: i64);
175
176    fn add_group(&self, group: &mut Group);
177    fn save_group(&self, group: &mut Group);
178    fn get_groups(&self, session_id: i64) -> Vec<Group>;
179    fn get_groups_complete(&self, session_id: i64) -> Vec<Group>;
180    fn get_group(&self, session_id: i64) -> Option<Group>;
181    fn group_has_protected_participations(&self, session_id: i64) -> bool;
182    fn get_group_complete(&self, group_id: i64) -> Option<Group>;
183
184    fn delete_user(&self, user_id: i64);
185    fn delete_all_users_for_group(&self, group_id: i64);
186    fn delete_group(&self, group_id: i64);
187    fn set_data_clearance_for_user(&self, user_id: i64, clearance_state: bool);
188    fn set_data_clearance_for_group(&self, group_id: i64, clearance_state: bool);
189    fn delete_participation(&self, user_id: i64, contest_id: i64);
190    fn remove_old_users_and_groups(&self, maxstudentage: time::Timespec, maxteacherage: Option<time::Timespec>,
191                                   maxage: Option<time::Timespec>)
192                                   -> Result<(i64, i64, i64, i64), ()>;
193    fn count_temporary_sessions(&self, maxage: time::Timespec) -> i64;
194    fn remove_temporary_sessions(&self, maxage: time::Timespec, limit: Option<i64>);
195
196    fn remove_autosaved_submissions(&self, maxage: time::Timespec, limit: Option<i64>);
197    fn remove_all_but_latest_submissions(&self, maxage: time::Timespec, limit: Option<i64>);
198
199    fn move_task_location(&self, old_location: &str, new_location: &str, contest: &str) -> Option<()>;
200    fn move_task_contest(&self, old_location: &str, new_location: &str, old_contest: &str, new_contest: &str)
201                         -> Option<()>;
202    fn move_contest_location(&self, old_location: &str, new_location: &str) -> Option<()>;
203    fn move_task_submissions_grades(&self, old_location: &str, new_location: &str, old_contest: &str,
204                                    new_contest: &str)
205                                    -> Option<()>;
206
207    fn get_search_users(
208        &self,
209        _: (Option<i64>,
210         Option<String>,
211         Option<String>,
212         Option<String>,
213         Option<String>,
214         Option<String>,
215         Option<String>,
216         Option<String>))
217        -> (Vec<(i64, Option<String>, Option<String>, Option<String>, Option<String>, Option<String>)>,
218            Vec<(i64, String, String, String)>);
219
220    fn get_debug_information(&self) -> String;
221
222    fn reset_all_contest_visibilities(&self);
223    fn reset_all_taskgroup_visibilities(&self);
224}
225
226pub trait MedalObject<T: MedalConnection> {
227    fn save(&mut self, conn: &T);
228}