Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

formatters.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. """
  2. """
  3. # Created on 2014.10.28
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2014 - 2018 Giovanni Cannata
  8. #
  9. # This file is part of ldap3.
  10. #
  11. # ldap3 is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU Lesser General Public License as published
  13. # by the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # ldap3 is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Lesser General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Lesser General Public License
  22. # along with ldap3 in the COPYING and COPYING.LESSER files.
  23. # If not, see <http://www.gnu.org/licenses/>.
  24. import re
  25. from binascii import hexlify
  26. from uuid import UUID
  27. from datetime import datetime, timedelta
  28. from ...utils.conv import to_unicode
  29. from ...core.timezone import OffsetTzInfo
  30. def format_unicode(raw_value):
  31. try:
  32. if str is not bytes: # Python 3
  33. return str(raw_value, 'utf-8', errors='strict')
  34. else: # Python 2
  35. return unicode(raw_value, 'utf-8', errors='strict')
  36. except (TypeError, UnicodeDecodeError):
  37. pass
  38. return raw_value
  39. def format_integer(raw_value):
  40. try:
  41. return int(raw_value)
  42. except (TypeError, ValueError): # expected exceptions
  43. pass
  44. except Exception: # any other exception should be investigated, anyway the formatter return the raw_value
  45. pass
  46. return raw_value
  47. def format_binary(raw_value):
  48. try:
  49. return bytes(raw_value)
  50. except TypeError: # expected exceptions
  51. pass
  52. except Exception: # any other exception should be investigated, anyway the formatter return the raw_value
  53. pass
  54. return raw_value
  55. def format_uuid(raw_value):
  56. try:
  57. return str(UUID(bytes=raw_value))
  58. except (TypeError, ValueError):
  59. return format_unicode(raw_value)
  60. except Exception: # any other exception should be investigated, anyway the formatter return the raw_value
  61. pass
  62. return raw_value
  63. def format_uuid_le(raw_value):
  64. try:
  65. return '{' + str(UUID(bytes_le=raw_value)) + '}'
  66. except (TypeError, ValueError):
  67. return format_unicode(raw_value)
  68. except Exception: # any other exception should be investigated, anyway the formatter return the raw_value
  69. pass
  70. return raw_value
  71. def format_boolean(raw_value):
  72. if raw_value in [b'TRUE', b'true', b'True']:
  73. return True
  74. if raw_value in [b'FALSE', b'false', b'False']:
  75. return False
  76. return raw_value
  77. def format_ad_timestamp(raw_value):
  78. """
  79. Active Directory stores date/time values as the number of 100-nanosecond intervals
  80. that have elapsed since the 0 hour on January 1, 1601 till the date/time that is being stored.
  81. The time is always stored in Greenwich Mean Time (GMT) in the Active Directory.
  82. """
  83. if raw_value == b'9223372036854775807': # max value to be stored in a 64 bit signed int
  84. return datetime.max # returns datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
  85. try:
  86. timestamp = int(raw_value)
  87. if timestamp < 0: # ad timestamp cannot be negative
  88. return raw_value
  89. except Exception:
  90. return raw_value
  91. try:
  92. return datetime.fromtimestamp(timestamp / 10000000.0 - 11644473600, tz=OffsetTzInfo(0, 'UTC')) # forces true division in python 2
  93. except (OSError, OverflowError, ValueError): # on Windows backwards timestamps are not allowed
  94. try:
  95. unix_epoch = datetime.fromtimestamp(0, tz=OffsetTzInfo(0, 'UTC'))
  96. diff_seconds = timedelta(seconds=timestamp/10000000.0 - 11644473600)
  97. return unix_epoch + diff_seconds
  98. except Exception:
  99. pass
  100. except Exception:
  101. pass
  102. return raw_value
  103. try: # uses regular expressions and the timezone class (python3.2 and later)
  104. from datetime import timezone
  105. time_format = re.compile(
  106. r'''
  107. ^
  108. (?P<Year>[0-9]{4})
  109. (?P<Month>0[1-9]|1[0-2])
  110. (?P<Day>0[1-9]|[12][0-9]|3[01])
  111. (?P<Hour>[01][0-9]|2[0-3])
  112. (?:
  113. (?P<Minute>[0-5][0-9])
  114. (?P<Second>[0-5][0-9]|60)?
  115. )?
  116. (?:
  117. [.,]
  118. (?P<Fraction>[0-9]+)
  119. )?
  120. (?:
  121. Z
  122. |
  123. (?:
  124. (?P<Offset>[+-])
  125. (?P<OffHour>[01][0-9]|2[0-3])
  126. (?P<OffMinute>[0-5][0-9])?
  127. )
  128. )
  129. $
  130. ''',
  131. re.VERBOSE
  132. )
  133. def format_time(raw_value):
  134. try:
  135. match = time_format.fullmatch(to_unicode(raw_value))
  136. if match is None:
  137. return raw_value
  138. matches = match.groupdict()
  139. offset = timedelta(
  140. hours=int(matches['OffHour'] or 0),
  141. minutes=int(matches['OffMinute'] or 0)
  142. )
  143. if matches['Offset'] == '-':
  144. offset *= -1
  145. # Python does not support leap second in datetime (!)
  146. if matches['Second'] == '60':
  147. matches['Second'] = '59'
  148. # According to RFC, fraction may be applied to an Hour/Minute (!)
  149. fraction = float('0.' + (matches['Fraction'] or '0'))
  150. if matches['Minute'] is None:
  151. fraction *= 60
  152. minute = int(fraction)
  153. fraction -= minute
  154. else:
  155. minute = int(matches['Minute'])
  156. if matches['Second'] is None:
  157. fraction *= 60
  158. second = int(fraction)
  159. fraction -= second
  160. else:
  161. second = int(matches['Second'])
  162. microseconds = int(fraction * 1000000)
  163. return datetime(
  164. int(matches['Year']),
  165. int(matches['Month']),
  166. int(matches['Day']),
  167. int(matches['Hour']),
  168. minute,
  169. second,
  170. microseconds,
  171. timezone(offset),
  172. )
  173. except Exception: # exceptions should be investigated, anyway the formatter return the raw_value
  174. pass
  175. return raw_value
  176. except ImportError:
  177. def format_time(raw_value):
  178. """
  179. From RFC4517:
  180. A value of the Generalized Time syntax is a character string
  181. representing a date and time. The LDAP-specific encoding of a value
  182. of this syntax is a restriction of the format defined in [ISO8601],
  183. and is described by the following ABNF:
  184. GeneralizedTime = century year month day hour
  185. [ minute [ second / leap-second ] ]
  186. [ fraction ]
  187. g-time-zone
  188. century = 2(%x30-39) ; "00" to "99"
  189. year = 2(%x30-39) ; "00" to "99"
  190. month = ( %x30 %x31-39 ) ; "01" (January) to "09"
  191. / ( %x31 %x30-32 ) ; "10" to "12"
  192. day = ( %x30 %x31-39 ) ; "01" to "09"
  193. / ( %x31-32 %x30-39 ) ; "10" to "29"
  194. / ( %x33 %x30-31 ) ; "30" to "31"
  195. hour = ( %x30-31 %x30-39 ) / ( %x32 %x30-33 ) ; "00" to "23"
  196. minute = %x30-35 %x30-39 ; "00" to "59"
  197. second = ( %x30-35 %x30-39 ) ; "00" to "59"
  198. leap-second = ( %x36 %x30 ) ; "60"
  199. fraction = ( DOT / COMMA ) 1*(%x30-39)
  200. g-time-zone = %x5A ; "Z"
  201. / g-differential
  202. g-differential = ( MINUS / PLUS ) hour [ minute ]
  203. MINUS = %x2D ; minus sign ("-")
  204. """
  205. if len(raw_value) < 10 or not all((c in b'0123456789+-,.Z' for c in raw_value)) or (b'Z' in raw_value and not raw_value.endswith(b'Z')): # first ten characters are mandatory and must be numeric or timezone or fraction
  206. return raw_value
  207. # sets position for fixed values
  208. year = int(raw_value[0: 4])
  209. month = int(raw_value[4: 6])
  210. day = int(raw_value[6: 8])
  211. hour = int(raw_value[8: 10])
  212. minute = 0
  213. second = 0
  214. microsecond = 0
  215. remain = raw_value[10:]
  216. if remain and remain.endswith(b'Z'): # uppercase 'Z'
  217. sep = b'Z'
  218. elif b'+' in remain: # timezone can be specified with +hh[mm] or -hh[mm]
  219. sep = b'+'
  220. elif b'-' in remain:
  221. sep = b'-'
  222. else: # timezone not specified
  223. return raw_value
  224. time, _, offset = remain.partition(sep)
  225. if time and (b'.' in time or b',' in time):
  226. # fraction time
  227. if time[0] in b',.':
  228. minute = 6 * int(time[1] if str is bytes else chr(time[1])) # Python 2 / Python 3
  229. elif time[2] in b',.':
  230. minute = int(raw_value[10: 12])
  231. second = 6 * int(time[3] if str is bytes else chr(time[3])) # Python 2 / Python 3
  232. elif time[4] in b',.':
  233. minute = int(raw_value[10: 12])
  234. second = int(raw_value[12: 14])
  235. microsecond = 100000 * int(time[5] if str is bytes else chr(time[5])) # Python 2 / Python 3
  236. elif len(time) == 2: # mmZ format
  237. minute = int(raw_value[10: 12])
  238. elif len(time) == 0: # Z format
  239. pass
  240. elif len(time) == 4: # mmssZ
  241. minute = int(raw_value[10: 12])
  242. second = int(raw_value[12: 14])
  243. else:
  244. return raw_value
  245. if sep == b'Z': # UTC
  246. timezone = OffsetTzInfo(0, 'UTC')
  247. else: # build timezone
  248. try:
  249. if len(offset) == 2:
  250. timezone_hour = int(offset[:2])
  251. timezone_minute = 0
  252. elif len(offset) == 4:
  253. timezone_hour = int(offset[:2])
  254. timezone_minute = int(offset[2:4])
  255. else: # malformed timezone
  256. raise ValueError
  257. except ValueError:
  258. return raw_value
  259. if timezone_hour > 23 or timezone_minute > 59: # invalid timezone
  260. return raw_value
  261. if str is not bytes: # Python 3
  262. timezone = OffsetTzInfo((timezone_hour * 60 + timezone_minute) * (1 if sep == b'+' else -1), 'UTC' + str(sep + offset, encoding='utf-8'))
  263. else: # Python 2
  264. timezone = OffsetTzInfo((timezone_hour * 60 + timezone_minute) * (1 if sep == b'+' else -1), unicode('UTC' + sep + offset, encoding='utf-8'))
  265. try:
  266. return datetime(year=year,
  267. month=month,
  268. day=day,
  269. hour=hour,
  270. minute=minute,
  271. second=second,
  272. microsecond=microsecond,
  273. tzinfo=timezone)
  274. except (TypeError, ValueError):
  275. pass
  276. return raw_value
  277. def format_time_with_0_year(raw_value):
  278. try:
  279. if raw_value.startswith(b'0000'):
  280. return raw_value
  281. except Exception:
  282. try:
  283. if raw_value.startswith('0000'):
  284. return raw_value
  285. except Exception:
  286. pass
  287. return format_time(raw_value)
  288. def format_sid(raw_value):
  289. """
  290. SID= "S-1-" IdentifierAuthority 1*SubAuthority
  291. IdentifierAuthority= IdentifierAuthorityDec / IdentifierAuthorityHex
  292. ; If the identifier authority is < 2^32, the
  293. ; identifier authority is represented as a decimal
  294. ; number
  295. ; If the identifier authority is >= 2^32,
  296. ; the identifier authority is represented in
  297. ; hexadecimal
  298. IdentifierAuthorityDec = 1*10DIGIT
  299. ; IdentifierAuthorityDec, top level authority of a
  300. ; security identifier is represented as a decimal number
  301. IdentifierAuthorityHex = "0x" 12HEXDIG
  302. ; IdentifierAuthorityHex, the top-level authority of a
  303. ; security identifier is represented as a hexadecimal number
  304. SubAuthority= "-" 1*10DIGIT
  305. ; Sub-Authority is always represented as a decimal number
  306. ; No leading "0" characters are allowed when IdentifierAuthority
  307. ; or SubAuthority is represented as a decimal number
  308. ; All hexadecimal digits must be output in string format,
  309. ; pre-pended by "0x"
  310. Revision (1 byte): An 8-bit unsigned integer that specifies the revision level of the SID. This value MUST be set to 0x01.
  311. SubAuthorityCount (1 byte): An 8-bit unsigned integer that specifies the number of elements in the SubAuthority array. The maximum number of elements allowed is 15.
  312. IdentifierAuthority (6 bytes): A SID_IDENTIFIER_AUTHORITY structure that indicates the authority under which the SID was created. It describes the entity that created the SID. The Identifier Authority value {0,0,0,0,0,5} denotes SIDs created by the NT SID authority.
  313. SubAuthority (variable): A variable length array of unsigned 32-bit integers that uniquely identifies a principal relative to the IdentifierAuthority. Its length is determined by SubAuthorityCount.
  314. """
  315. try:
  316. if str is not bytes: # Python 3
  317. revision = int(raw_value[0])
  318. sub_authority_count = int(raw_value[1])
  319. identifier_authority = int.from_bytes(raw_value[2:8], byteorder='big')
  320. if identifier_authority >= 4294967296: # 2 ^ 32
  321. identifier_authority = hex(identifier_authority)
  322. sub_authority = ''
  323. i = 0
  324. while i < sub_authority_count:
  325. sub_authority += '-' + str(int.from_bytes(raw_value[8 + (i * 4): 12 + (i * 4)], byteorder='little')) # little endian
  326. i += 1
  327. else: # Python 2
  328. revision = int(ord(raw_value[0]))
  329. sub_authority_count = int(ord(raw_value[1]))
  330. identifier_authority = int(hexlify(raw_value[2:8]), 16)
  331. if identifier_authority >= 4294967296: # 2 ^ 32
  332. identifier_authority = hex(identifier_authority)
  333. sub_authority = ''
  334. i = 0
  335. while i < sub_authority_count:
  336. sub_authority += '-' + str(int(hexlify(raw_value[11 + (i * 4): 7 + (i * 4): -1]), 16)) # little endian
  337. i += 1
  338. return 'S-' + str(revision) + '-' + str(identifier_authority) + sub_authority
  339. except Exception: # any exception should be investigated, anyway the formatter return the raw_value
  340. pass
  341. return raw_value