'update'
[qomet.git] / public / javascripts / utils / validation.js
... / ...
CommitLineData
1try { var _ = require("underscore"); } catch (err) {} //for server
2
3let Validator = { };
4
5// Cell in evaluation.questions array
6Validator.Question = {
7 "index": "section", //"2.2.1", "3.2", "1" ...etc
8 "wording": "string",
9 "options": "stringArray", //only for quiz
10 "fixed": "boolean",
11 "points": "number",
12};
13
14Validator.Answer = {
15 "index": "section",
16 "value": "stringOrIntegerArray",
17};
18
19Validator.Input = {
20 "index": "section",
21 "input": "stringOrIntegerArray",
22};
23
24// One student response to an exam
25Validator.Paper = {
26 "number": "code",
27 "inputs": Validator.Input,
28 "startTime": "positiveInteger",
29 "current": "positiveInteger",
30 "discoTime": "positiveInteger",
31 "discoCount": "positiveInteger",
32 "totalDisco": "positiveInteger",
33 "password": "password",
34};
35
36Validator.Evaluation = {
37 "_id": "bson",
38 "cid": "bson",
39 "name": "code",
40 "mode": "alphanumeric", //"open" or "exam", but alphanumeric is good enough
41 "active": "boolean",
42 "fixed": "boolean",
43 "display": "alphanumeric", //"one" or "all"
44 "time": "integer",
45 "introduction": "string",
46 "coefficient": "number",
47 "questions": Validator.Question,
48 "answers": Validator.Answer,
49 "papers": Validator.Paper,
50};
51
52Validator.User = {
53 "_id": "bson",
54 "email": "email",
55 "name": "name",
56 "initials": "alphanumeric",
57 "loginToken": "alphanumeric",
58 "sessionTokens": "alphanumericArray",
59};
60
61Validator.Student = {
62 "number": "code",
63 "name": "name",
64 "group": "positiveInteger",
65};
66
67Validator.Course = {
68 "_id": "bson",
69 "uid": "bson",
70 "code": "code",
71 "description": "string",
72 "password": "hash",
73 "students": Validator.Student,
74};
75
76Object.assign(Validator,
77{
78 // Recurse into sub-documents
79 checkObject_aux: function(obj, model)
80 {
81 for (let key of Object.keys(obj))
82 {
83 if (!model[key])
84 return "Unknown field";
85 if (model[key] == "unchecked") //not a user input (ignored)
86 continue;
87 if (_.isObject(model[key]))
88 {
89 // TODO: next loop seems too heavy... (only a concern if big class import?)
90 for (let item of obj[key])
91 {
92 let error = Validator.checkObject_aux(item, model[key]);
93 if (error.length > 0)
94 return error;
95 }
96 }
97 else
98 {
99 let error = Validator[ "check_" + model[key] ](obj[key]);
100 if (error.length > 0)
101 return key + ": " + error;
102 }
103 }
104 return "";
105 },
106
107 // Always check top-level object
108 checkObject: function(obj, name)
109 {
110 return Validator.checkObject_aux(obj, Validator[name]);
111 },
112
113 "check_string": function(arg)
114 {
115 return ""; //strings are unchecked, but sanitized
116 },
117
118 "check_section": function(arg)
119 {
120 if (!_.isString(arg))
121 return "not a string";
122 if (!/^[0-9.]+$/.test(arg))
123 return "digits and dot only";
124 return "";
125 },
126
127 "check_alphanumeric": function(arg)
128 {
129 return arg.match(/^[\w]{1,32}$/) === null ? "[1,32] alphanumerics" : "";
130 },
131
132 "check_bson": function(arg)
133 {
134 return arg.match(/^[a-z0-9]{24}$/) === null ? "not a BSON id" : "";
135 },
136
137 "check_name": function(arg)
138 {
139 if (!_.isString(arg))
140 return "not a string";
141 if (!/^[a-zA-Z\u00C0-\u024F -]{1,32}$/.test(arg))
142 return "[1,32] letters + hyphen/space";
143 return "";
144 },
145
146 "check_email": function(arg)
147 {
148 if (!_.isString(arg))
149 return "not a string";
150 if (arg.length > 64)
151 return "string too long: max. 64 characters";
152 // Regexp used in "type='email'" inputs ( http://emailregex.com/ )
153 if (!/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(arg))
154 return "X@Y, alphanumerics and . _ - +(X)";
155 return "";
156 },
157
158 "check_code": function(arg)
159 {
160 if (!_.isString(arg))
161 return "not a string";
162 if (!/^[\w.-]{1,16}$/.test(arg))
163 return "[1,16] alphanumerics and . _ -";
164 return "";
165 },
166
167 "check_number": function(arg)
168 {
169 if (!_.isNumber(arg))
170 arg = parseFloat(arg);
171 if (isNaN(arg))
172 return "not a number";
173 return "";
174 },
175
176 "check_integer": function(arg)
177 {
178 if (!_.isNumber(arg))
179 arg = parseInt(arg);
180 if (isNaN(arg) || arg % 1 != 0)
181 return "not an integer";
182 return "";
183 },
184
185 "check_positiveInteger": function(arg)
186 {
187 return Validator["check_integer"](arg) || (arg<0 ? "not positive" : "");
188 },
189
190 "check_boolean": function(arg)
191 {
192 if (!_.isBoolean(arg))
193 return "not a boolean";
194 return "";
195 },
196
197 "check_password": function(arg)
198 {
199 if (!_.isString(arg))
200 return "not a string";
201 if (!/^[\x21-\x7E]{1,16}$/.test(arg))
202 return "{1,16} ASCII characters with code in [33,126]";
203 return "";
204 },
205
206 // Sha-1 hash: length 40, hexadecimal
207 "check_hash": function(arg)
208 {
209 if (!_.isString(arg))
210 return "not a string";
211 if (!/^[a-f0-9]{40}$/.test(arg))
212 return "not a sha-1 hash";
213 return "";
214 },
215
216 "check_integerArray": function(arg)
217 {
218 if (!_.isArray(arg))
219 return "not an array";
220 for (let i=0; i<arg.length; i++)
221 {
222 let error = Validator["check_integer"](arg[i]);
223 if (error.length > 0)
224 return error;
225 }
226 return "";
227 },
228
229 "check_stringArray": function(arg)
230 {
231 if (!_.isArray(arg))
232 return "not an array";
233 for (let i=0; i<arg.length; i++)
234 {
235 if (!_.isString(arg[i]));
236 return "not a string";
237 }
238 return "";
239 },
240
241 "check_alphanumericArray": function(arg)
242 {
243 if (!_.isArray(arg))
244 return "not an array";
245 for (let i=0; i<arg.length; i++)
246 {
247 let error = Validator["check_alphanumeric"](arg[i]);
248 if (error.length > 0)
249 return error;
250 }
251 return "";
252 },
253
254 "check_stringOrIntegerArray": function(arg)
255 {
256 if (!_.isString(arg))
257 return Validator["check_integerArray"](arg);
258 return "";
259 },
260});
261
262try { module.exports = Validator.checkObject; } catch (err) { } //for server