Commit | Line | Data |
---|---|---|
ea8b41a9 | 1 | try { var _ = require("underscore"); } catch (err) { } //for server |
e99c53fb BA |
2 | |
3 | let Validator = { }; | |
4 | ||
a3080c33 | 5 | // Cell in evaluation.questions array |
e99c53fb BA |
6 | Validator.Question = { |
7 | "index": "section", //"2.2.1", "3.2", "1" ...etc | |
8 | "wording": "string", | |
9 | "options": "stringArray", //only for quiz | |
10 | "fixed": "boolean", | |
e99c53fb BA |
11 | "points": "number", |
12 | }; | |
13 | ||
14c9c66a BA |
14 | Validator.Answer = { |
15 | "index": "section", | |
16 | "value": "stringOrIntegerArray", | |
17 | }; | |
18 | ||
e99c53fb BA |
19 | Validator.Input = { |
20 | "index": "section", | |
21 | "input": "stringOrIntegerArray", | |
22 | }; | |
23 | ||
24 | // One student response to an exam | |
25 | Validator.Paper = { | |
26 | "number": "code", | |
e99c53fb BA |
27 | "inputs": Validator.Input, |
28 | "startTime": "positiveInteger", | |
14c9c66a | 29 | "current": "positiveInteger", |
29c8b391 BA |
30 | "discoTime": "positiveInteger", |
31 | "discoCount": "positiveInteger", | |
32 | "totalDisco": "positiveInteger", | |
e99c53fb BA |
33 | "password": "password", |
34 | }; | |
35 | ||
a3080c33 | 36 | Validator.Evaluation = { |
e99c53fb BA |
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", | |
e99c53fb BA |
46 | "coefficient": "number", |
47 | "questions": Validator.Question, | |
14c9c66a | 48 | "answers": Validator.Answer, |
e99c53fb BA |
49 | "papers": Validator.Paper, |
50 | }; | |
51 | ||
52 | Validator.User = { | |
53 | "_id": "bson", | |
54 | "email": "email", | |
e99c53fb | 55 | "name": "name", |
14c9c66a BA |
56 | "initials": "alphanumeric", |
57 | "loginToken": "alphanumeric", | |
58 | "sessionTokens": "alphanumericArray", | |
e99c53fb BA |
59 | }; |
60 | ||
61 | Validator.Student = { | |
62 | "number": "code", | |
e99c53fb BA |
63 | "name": "name", |
64 | "group": "positiveInteger", | |
65 | }; | |
66 | ||
67 | Validator.Course = { | |
68 | "_id": "bson", | |
69 | "uid": "bson", | |
70 | "code": "code", | |
71 | "description": "string", | |
72 | "password": "hash", | |
73 | "students": Validator.Student, | |
74 | }; | |
75 | ||
76 | Object.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"; | |
e99c53fb BA |
85 | if (_.isObject(model[key])) |
86 | { | |
87 | // TODO: next loop seems too heavy... (only a concern if big class import?) | |
88 | for (let item of obj[key]) | |
89 | { | |
90 | let error = Validator.checkObject_aux(item, model[key]); | |
91 | if (error.length > 0) | |
92 | return error; | |
93 | } | |
94 | } | |
95 | else | |
96 | { | |
97 | let error = Validator[ "check_" + model[key] ](obj[key]); | |
98 | if (error.length > 0) | |
99 | return key + ": " + error; | |
100 | } | |
101 | } | |
102 | return ""; | |
103 | }, | |
104 | ||
105 | // Always check top-level object | |
106 | checkObject: function(obj, name) | |
107 | { | |
108 | return Validator.checkObject_aux(obj, Validator[name]); | |
109 | }, | |
110 | ||
111 | "check_string": function(arg) | |
112 | { | |
ea8b41a9 BA |
113 | if (!_.isString(arg)) |
114 | return "not a string"; | |
e99c53fb BA |
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 | ||
e99c53fb BA |
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)) | |
14c9c66a | 202 | return "{1,16} ASCII characters with code in [33,126]"; |
e99c53fb BA |
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 | ||
14c9c66a BA |
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 | ||
e99c53fb BA |
254 | "check_stringOrIntegerArray": function(arg) |
255 | { | |
256 | if (!_.isString(arg)) | |
257 | return Validator["check_integerArray"](arg); | |
258 | return ""; | |
259 | }, | |
260 | }); | |
261 | ||
14c9c66a | 262 | try { module.exports = Validator.checkObject; } catch (err) { } //for server |