PageRenderTime 38ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/eea/usersdb/schema.py

https://github.com/eea/eea.usersdb
Python | 140 lines | 125 code | 10 blank | 5 comment | 7 complexity | 5bd40ea28d6581ce47e651ac14ba8bca MD5 | raw file
  1. ''' users db schema '''
  2. import re
  3. from six.moves import range
  4. import colander
  5. import phonenumbers
  6. INVALID_PHONE_MESSAGES = (
  7. ("Invalid telephone number. It must be written "
  8. "using international notation, starting with \"+\"."),
  9. ("This does not appear to be a valid phone number given the "
  10. "country / area code provided. If you second check and believe "
  11. "the number is correct, please contact HelpDesk.")
  12. )
  13. INVALID_EMAIL = "Invalid email format %s"
  14. NUMBER_FORMAT = phonenumbers.PhoneNumberFormat.INTERNATIONAL
  15. INVALID_STRING_ENCODING = ('%s must be written in latin characters')
  16. class PhoneNumber(colander.String):
  17. """PhoneNumber type for colander Node"""
  18. def serialize(self, node, appstruct):
  19. ''' serialize '''
  20. if appstruct is colander.null or not appstruct:
  21. return colander.null
  22. return appstruct
  23. def deserialize(self, node, cstruct):
  24. ''' deserialize '''
  25. try:
  26. number = phonenumbers.parse(cstruct)
  27. except Exception:
  28. return cstruct
  29. else:
  30. return phonenumbers.format_number(number, NUMBER_FORMAT)
  31. def cstruct_children(self, *args):
  32. ''' just return an empty list? '''
  33. return []
  34. def _phone_validator(node, value):
  35. """Check if provided number is possible number"""
  36. if not value:
  37. return
  38. try:
  39. number = phonenumbers.parse(value)
  40. except Exception:
  41. raise colander.Invalid(node, INVALID_PHONE_MESSAGES[0])
  42. else:
  43. if not phonenumbers.is_possible_number(number):
  44. raise colander.Invalid(node, INVALID_PHONE_MESSAGES[1])
  45. def _latin_validator(node, value):
  46. """Check if provided string is written with latin-based characters"""
  47. if not value:
  48. return
  49. for letter in value:
  50. for index in range(1, 11):
  51. try:
  52. letter.encode('latin%s' % index)
  53. except UnicodeEncodeError:
  54. pass
  55. else:
  56. break
  57. else:
  58. raise colander.Invalid(node,
  59. INVALID_STRING_ENCODING % node.description)
  60. INVALID_URL = "Invalid URL. It must begin with \"http://\" or \"https://\"."
  61. # max length for domain name labels is 63 characters per RFC 1034
  62. _url_validator = colander.Regex(r'^http[s]?\://', msg=INVALID_URL)
  63. def _email_validator(node, value):
  64. """ email validator """
  65. pattern = (r"(?:^|\s)[-a-z-A-Z0-9_.']+@(?:[-a-z-A-Z0-9]+\.)+[a-z-A-Z]"
  66. r"{2,63}(?:\s|$)")
  67. if not re.match(pattern, value):
  68. raise colander.Invalid(node, INVALID_EMAIL % value)
  69. class UserInfoSchema(colander.MappingSchema):
  70. """
  71. Schema for Eionet LDAP user information. Can be used by front-end tools
  72. to verify data before sending it to `eea.usersdb`. The `eea.usersdb`
  73. library does very little validation of its own.
  74. """
  75. first_name = colander.SchemaNode(
  76. colander.String(), validator=_latin_validator,
  77. description='First name')
  78. last_name = colander.SchemaNode(
  79. colander.String(), validator=_latin_validator, description='Last name')
  80. full_name_native = colander.SchemaNode(
  81. colander.String(), missing='', validator=_latin_validator,
  82. description='Full name (native language)')
  83. search_helper = colander.SchemaNode(
  84. colander.String(), missing='', description='ASCII search helper')
  85. reasonToCreate = colander.SchemaNode(
  86. colander.String(), description='Reason to create the account')
  87. job_title = colander.SchemaNode(
  88. colander.String(), missing='', description='Job title')
  89. email = colander.SchemaNode(
  90. colander.String(), validator=_email_validator, description='E-mail')
  91. url = colander.SchemaNode(
  92. colander.String(), missing='', validator=_url_validator,
  93. description='URL')
  94. postal_address = colander.SchemaNode(
  95. colander.String(), missing='', description='Postal address')
  96. phone = colander.SchemaNode(
  97. PhoneNumber(), missing='', validator=_phone_validator,
  98. description='Telephone number')
  99. mobile = colander.SchemaNode(
  100. PhoneNumber(), missing='', validator=_phone_validator,
  101. description='Mobile telephone number')
  102. fax = colander.SchemaNode(
  103. PhoneNumber(), missing='', validator=_phone_validator,
  104. description='Fax number')
  105. organisation = colander.SchemaNode(
  106. colander.String(), description='Organisation')
  107. department = colander.SchemaNode(
  108. colander.String(), missing='', description='Department')
  109. user_info_schema = UserInfoSchema()
  110. # These can be used in register/create user forms:
  111. INVALID_USERNAME = ("Invalid username. It must contain only digits, lowercase "
  112. "letters and/or _ (underscore).")
  113. _uid_node = colander.SchemaNode(colander.String(), name='id',
  114. description="User ID")
  115. _uid_node.validator = colander.Regex(r'^[a-z0-9_]+$', msg=INVALID_USERNAME)
  116. _password_node = colander.SchemaNode(colander.String(), name='password',
  117. description="Login password")