Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

import imghdr 

import xml.etree.cElementTree as et 

 

from django.core.exceptions import ValidationError 

from django.utils.deconstruct import deconstructible 

from jsonschema import FormatChecker 

from jsonschema.exceptions import ValidationError as JsonValidationError 

from jsonschema.validators import validator_for 

 

from backend_app.validation.schemas import ( 

USEFUL_LINKS_SCHEMA, 

PHOTOS_SCHEMA, 

THEME_SCHEMA, 

RECOMMENDATION_LIST_CONTENT_SCHEMA, 

) 

from backend_app.validation.utils import DEFINITIONS_RESOLVER 

 

# Format checker to use with jsonschemas to make sure errors are thrown 

FORMAT_CHECKER = FormatChecker(["uri"]) 

 

 

class JsonValidator(object): 

""" 

Class to hold the similar logic between the classes that are used validate JSON 

""" 

 

# Value to override 

schema = None 

 

def __init__(self): 

self.validator = validator_for(self.schema)( 

self.schema, resolver=DEFINITIONS_RESOLVER, format_checker=FORMAT_CHECKER 

) 

 

def __call__(self, value): 

""" 

Perform validation 

 

:param value: Value to validate 

:type value: list, dict 

:raises: ValidationErrort 

""" 

try: 

self.validator.validate(value) 

except JsonValidationError as e: 

raise ValidationError(e.message) 

 

 

@deconstructible() 

class UsefulLinksValidator(JsonValidator): 

""" 

Validator to be used on a JSON field that is supposed to store Useful links 

""" 

 

schema = USEFUL_LINKS_SCHEMA 

 

 

@deconstructible() 

class PhotosValidator(JsonValidator): 

""" 

Validator to be used on a JSON field that is supposed to store photos 

""" 

 

schema = PHOTOS_SCHEMA 

 

 

@deconstructible() 

class ThemeValidator(JsonValidator): 

""" 

Validator to be used on a JSON field that is supposed to store the theme data 

""" 

 

schema = THEME_SCHEMA 

 

 

@deconstructible() 

class RecommendationListJsonContentValidator(JsonValidator): 

""" 

Validator to be used on a JSON field that is supposed to store recommendation list content 

""" 

 

schema = RECOMMENDATION_LIST_CONTENT_SCHEMA 

 

 

@deconstructible() 

class RecommendationListUnivValidator(object): 

""" 

Validator to check that all the universities exist in the json of recommendation list 

""" 

 

def __init__(self): 

from backend_app.models.university import University 

 

self.University = University 

 

@staticmethod 

def get_universities_ids_from_content(content): 

univ_ids = set() 

for block in content: 

if block["type"] == "univ-block": 

univ_ids.add(block["content"]["university"]) 

return univ_ids 

 

def __call__(self, value): 

univ_ids_in_content = self.get_universities_ids_from_content(value) 

all_universities_ids = set( 

map(lambda univ: univ.pk, self.University.objects.all()) 

) 

 

for univ_id in univ_ids_in_content: 

if univ_id not in all_universities_ids: 

raise ValidationError("Unrecognized university id {}".format(univ_id)) 

 

 

@deconstructible() 

class PathExtensionValidator(object): 

""" 

Validator to be used to check if a string ends with .(ext) from a list 

of allowed extensions. 

""" 

 

def __init__(self, allowed_extensions): 

""" 

:param allowed_extensions: 

:type allowed_extensions: iterable 

""" 

self.allowed_extensions = [ext.lower() for ext in allowed_extensions] 

 

def __call__(self, string): 

""" 

Perform validation 

 

:param string: Value to validate 

:type string: str 

:raises: ValidationError 

""" 

try: 

if string.split(".")[-1].lower() not in self.allowed_extensions: 

raise ValidationError( 

"The file you submitted has an unauthorized extension" 

) 

except KeyError: 

raise ValidationError("File extension not recognized") 

 

 

@deconstructible() 

class ImageValidator(object): 

""" 

Validator to be check that a file is a valid image. 

Can't be tricked, definitely bulletproof. 

""" 

 

# Plus svg 

allowed_format = ["jpeg", "png", "webp"] 

 

def __call__(self, file): 

self.is_image(file) 

 

def is_image(self, fp): 

if imghdr.what(fp) not in self.allowed_format: 

if type(fp) is str: 

with open(fp, "r") as f: 

if not self.is_svg(f): 

raise ValidationError("Image not recognized") 

else: 

if not self.is_svg(fp): 

raise ValidationError("Image not recognized") 

 

@staticmethod 

def is_svg(f): 

""" 

Check if the provided file is svg 

""" 

f.seek(0) 

tag = None 

try: 

for event, el in et.iterparse(f, ("start",)): 

tag = el.tag 

break 

except et.ParseError: 

pass 

return tag == "{http://www.w3.org/2000/svg}svg"