Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
validators.py503 linesDownload Raw Back to formatters
1"""2"""3 4# Created on 2016.08.095#6# Author: Giovanni Cannata7#8# Copyright 2016 - 2020 Giovanni Cannata9#10# This file is part of ldap3.11#12# ldap3 is free software: you can redistribute it and/or modify13# it under the terms of the GNU Lesser General Public License as published14# by the Free Software Foundation, either version 3 of the License, or15# (at your option) any later version.16#17# ldap3 is distributed in the hope that it will be useful,18# but WITHOUT ANY WARRANTY; without even the implied warranty of19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the20# GNU Lesser General Public License for more details.21#22# You should have received a copy of the GNU Lesser General Public License23# along with ldap3 in the COPYING and COPYING.LESSER files.24# If not, see <http://www.gnu.org/licenses/>.25from binascii import a2b_hex, hexlify26from datetime import datetime27from calendar import timegm28from uuid import UUID29from struct import pack30 31 32from ... import SEQUENCE_TYPES, STRING_TYPES, NUMERIC_TYPES, INTEGER_TYPES33from .formatters import format_time, format_ad_timestamp34from ...utils.conv import to_raw, to_unicode, ldap_escape_to_bytes, escape_bytes35 36# Validators return True if value is valid, False if value is not valid,37# or a value different from True and False that is a valid value to substitute to the input value38 39 40def check_backslash(value):41    if isinstance(value, (bytearray, bytes)):42        if b'\\' in value:43            value = value.replace(b'\\', b'\\5C')44    elif isinstance(value, STRING_TYPES):45        if '\\' in value:46            value = value.replace('\\', '\\5C')47    return value48 49 50def check_type(input_value, value_type):51    if isinstance(input_value, value_type):52        return True53 54    if isinstance(input_value, SEQUENCE_TYPES):55        for value in input_value:56            if not isinstance(value, value_type):57                return False58        return True59 60    return False61 62 63# noinspection PyUnusedLocal64def always_valid(input_value):65    return True66 67 68def validate_generic_single_value(input_value):69    if not isinstance(input_value, SEQUENCE_TYPES):70        return True71 72    try:  # object couldn't have a __len__ method73        if len(input_value) == 1:74            return True75    except Exception:76        pass77 78    return False79 80 81def validate_zero_and_minus_one_and_positive_int(input_value):82    """Accept -1 and 0 only (used by pwdLastSet in AD)83    """84    if not isinstance(input_value, SEQUENCE_TYPES):85        if isinstance(input_value, NUMERIC_TYPES) or isinstance(input_value, STRING_TYPES):86            return True if int(input_value) >= -1 else False87        return False88    else:89        if len(input_value) == 1 and (isinstance(input_value[0], NUMERIC_TYPES) or isinstance(input_value[0], STRING_TYPES)):90            return True if int(input_value[0]) >= -1 else False91 92    return False93 94 95def validate_integer(input_value):96    if check_type(input_value, (float, bool)):97        return False98    if check_type(input_value, INTEGER_TYPES):99        return True100 101    if not isinstance(input_value, SEQUENCE_TYPES):102        sequence = False103        input_value = [input_value]104    else:105        sequence = True  # indicates if a sequence must be returned106 107    valid_values = []  # builds a list of valid int values108    from decimal import Decimal, InvalidOperation109    for element in input_value:110        try:  #try to convert any type to int, an invalid conversion raise TypeError or ValueError, doublecheck with Decimal type, if both are valid and equal then then int() value is used111            value = to_unicode(element) if isinstance(element, bytes) else element112            decimal_value = Decimal(value)113            int_value = int(value)114            if decimal_value == int_value:115                valid_values.append(int_value)116            else:117                return False118        except (ValueError, TypeError, InvalidOperation):119            return False120 121    if sequence:122        return valid_values123    else:124        return valid_values[0]125 126 127def validate_bytes(input_value):128    return check_type(input_value, bytes)129 130 131def validate_boolean(input_value):132    # it could be a real bool or the string TRUE or FALSE, # only a single valued is allowed133    if validate_generic_single_value(input_value):  # valid only if a single value or a sequence with a single element134        if isinstance(input_value, SEQUENCE_TYPES):135            input_value = input_value[0]136        if isinstance(input_value, bool):137            if input_value:138                return 'TRUE'139            else:140                return 'FALSE'141        if str is not bytes and isinstance(input_value, bytes):  # python3 try to converts bytes to string142            input_value = to_unicode(input_value)143        if isinstance(input_value, STRING_TYPES):144            if input_value.lower() == 'true':145                return 'TRUE'146            elif input_value.lower() == 'false':147                return 'FALSE'148    return False149 150 151def validate_time_with_0_year(input_value):152    # validates generalized time but accept a 0000 year too153    # if datetime object doesn't have a timezone it's considered local time and is adjusted to UTC154    if not isinstance(input_value, SEQUENCE_TYPES):155        sequence = False156        input_value = [input_value]157    else:158        sequence = True  # indicates if a sequence must be returned159 160    valid_values = []161    changed = False162    for element in input_value:163        if str is not bytes and isinstance(element, bytes):  # python3 try to converts bytes to string164            element = to_unicode(element)165        if isinstance(element, STRING_TYPES):  # tries to check if it is already be a Generalized Time166            if element.startswith('0000') or isinstance(format_time(to_raw(element)), datetime):  # valid Generalized Time string167                valid_values.append(element)168            else:169                return False170        elif isinstance(element, datetime):171            changed = True172            if element.tzinfo:  # a datetime with a timezone173                valid_values.append(element.strftime('%Y%m%d%H%M%S%z'))174            else:  # datetime without timezone, assumed local and adjusted to UTC175                offset = datetime.now() - datetime.utcnow()176                valid_values.append((element - offset).strftime('%Y%m%d%H%M%SZ'))177        else:178            return False179 180    if changed:181        if sequence:182            return valid_values183        else:184            return valid_values[0]185    else:186        return True187 188 189def validate_time(input_value):190    # if datetime object doesn't have a timezone it's considered local time and is adjusted to UTC191    if not isinstance(input_value, SEQUENCE_TYPES):192        sequence = False193        input_value = [input_value]194    else:195        sequence = True  # indicates if a sequence must be returned196 197    valid_values = []198    changed = False199    for element in input_value:200        if str is not bytes and isinstance(element, bytes):  # python3 try to converts bytes to string201            element = to_unicode(element)202        if isinstance(element, STRING_TYPES):  # tries to check if it is already be a Generalized Time203            if isinstance(format_time(to_raw(element)), datetime):  # valid Generalized Time string204                valid_values.append(element)205            else:206                return False207        elif isinstance(element, datetime):208            changed = True209            if element.tzinfo:  # a datetime with a timezone210                valid_values.append(element.strftime('%Y%m%d%H%M%S%z'))211            else:  # datetime without timezone, assumed local and adjusted to UTC212                offset = datetime.now() - datetime.utcnow()213                valid_values.append((element - offset).strftime('%Y%m%d%H%M%SZ'))214        else:215            return False216 217    if changed:218        if sequence:219            return valid_values220        else:221            return valid_values[0]222    else:223        return True224 225 226def validate_ad_timestamp(input_value):227    """228    Active Directory stores date/time values as the number of 100-nanosecond intervals229    that have elapsed since the 0 hour on January 1, 1601 till the date/time that is being stored.230    The time is always stored in Greenwich Mean Time (GMT) in the Active Directory.231    """232    if not isinstance(input_value, SEQUENCE_TYPES):233        sequence = False234        input_value = [input_value]235    else:236        sequence = True  # indicates if a sequence must be returned237 238    valid_values = []239    changed = False240    for element in input_value:241        if str is not bytes and isinstance(element, bytes):  # python3 try to converts bytes to string242            element = to_unicode(element)243        if isinstance(element, NUMERIC_TYPES):244            if 0 <= element <= 9223372036854775807:  # min and max for the AD timestamp starting from 12:00 AM January 1, 1601245                valid_values.append(element)246            else:247                return False248        elif isinstance(element, STRING_TYPES):  # tries to check if it is already be a AD timestamp249            if isinstance(format_ad_timestamp(to_raw(element)), datetime):  # valid Generalized Time string250                valid_values.append(element)251            else:252                return False253        elif isinstance(element, datetime):254            changed = True255            if element.tzinfo:  # a datetime with a timezone256                valid_values.append(to_raw((timegm(element.utctimetuple()) + 11644473600) * 10000000, encoding='ascii'))257            else:  # datetime without timezone, assumed local and adjusted to UTC258                offset = datetime.now() - datetime.utcnow()259                valid_values.append(to_raw((timegm((element - offset).timetuple()) + 11644473600) * 10000000, encoding='ascii'))260        else:261            return False262 263    if changed:264        if sequence:265            return valid_values266        else:267            return valid_values[0]268    else:269        return True270 271 272def validate_ad_timedelta(input_value):273    """274    Should be validated like an AD timestamp except that since it is a time275    delta, it is stored as a negative number.276    """277    if not isinstance(input_value, INTEGER_TYPES) or input_value > 0:278        return False279    return validate_ad_timestamp(input_value * -1)280 281 282def validate_guid(input_value):283    """284    object guid in uuid format (Novell eDirectory)285    """286    if not isinstance(input_value, SEQUENCE_TYPES):287        sequence = False288        input_value = [input_value]289    else:290        sequence = True  # indicates if a sequence must be returned291 292    valid_values = []293    changed = False294    for element in input_value:295        if isinstance(element,  STRING_TYPES):296            try:297                valid_values.append(UUID(element).bytes)298                changed = True299            except ValueError: # try if the value is an escaped ldap byte sequence300                try:301                    x = ldap_escape_to_bytes(element)302                    valid_values.append(UUID(bytes=x).bytes)303                    changed = True304                    continue305                except ValueError:306                    if str is not bytes:  # python 3307                        pass308                    else:309                        valid_values.append(element)310                        continue311                return False312        elif isinstance(element, (bytes, bytearray)):  # assumes bytes are valid313            valid_values.append(element)314        else:315            return False316 317    if changed:318        # valid_values = [check_backslash(value) for value in valid_values]319        if sequence:320            return valid_values321        else:322            return valid_values[0]323    else:324        return True325 326 327def validate_uuid(input_value):328    """329    object entryUUID in uuid format330    """331    if not isinstance(input_value, SEQUENCE_TYPES):332        sequence = False333        input_value = [input_value]334    else:335        sequence = True  # indicates if a sequence must be returned336 337    valid_values = []338    changed = False339    for element in input_value:340        if isinstance(element,  STRING_TYPES):341            try:342                valid_values.append(str(UUID(element)))343                changed = True344            except ValueError: # try if the value is an escaped byte sequence345                try:346                    valid_values.append(str(UUID(element.replace('\\', ''))))347                    changed = True348                    continue349                except ValueError:350                    if str is not bytes:  # python 3351                        pass352                    else:353                        valid_values.append(element)354                        continue355                return False356        elif isinstance(element, (bytes, bytearray)):  # assumes bytes are valid357            valid_values.append(element)358        else:359            return False360 361    if changed:362        # valid_values = [check_backslash(value) for value in valid_values]363        if sequence:364            return valid_values365        else:366            return valid_values[0]367    else:368        return True369 370 371def validate_uuid_le(input_value):372    r"""373    Active Directory stores objectGUID in uuid_le format, follows RFC4122 and MS-DTYP:374    "{07039e68-4373-264d-a0a7-07039e684373}": string representation big endian, converted to little endian (with or without brace curles)375    "689e030773434d26a7a007039e684373": packet representation, already in little endian376    "\68\9e\03\07\73\43\4d\26\a7\a0\07\03\9e\68\43\73": bytes representation, already in little endian377    byte sequence: already in little endian378 379    """380    if not isinstance(input_value, SEQUENCE_TYPES):381        sequence = False382        input_value = [input_value]383    else:384        sequence = True  # indicates if a sequence must be returned385 386    valid_values = []387    changed = False388    for element in input_value:389        error = False390        if isinstance(element, STRING_TYPES):391            if element[0] == '{' and element[-1] == '}':392                try:393                    valid_values.append(UUID(hex=element).bytes_le)  # string representation, value in big endian, converts to little endian394                    changed = True395                except ValueError:396                    error = True397            elif '-' in element:398                try:399                    valid_values.append(UUID(hex=element).bytes_le)  # string representation, value in big endian, converts to little endian400                    changed = True401                except ValueError:402                    error = True403            elif '\\' in element:404                try:405                    valid_values.append(UUID(bytes_le=ldap_escape_to_bytes(element)).bytes_le)  # byte representation, value in little endian406                    changed = True407                except ValueError:408                    error = True409            elif '-' not in element:  # value in little endian410                try:411                    valid_values.append(UUID(bytes_le=a2b_hex(element)).bytes_le)  # packet representation, value in little endian, converts to little endian412                    changed = True413                except ValueError:414                    error = True415            if error and (str is bytes):  # python2 only assume value is bytes and valid416                valid_values.append(element)  # value is untouched, must be in little endian417        elif isinstance(element, (bytes, bytearray)):  # assumes bytes are valid uuid418            valid_values.append(element)  # value is untouched, must be in little endian419        else:420            return False421 422    if changed:423        # valid_values = [check_backslash(value) for value in valid_values]424        if sequence:425            return valid_values426        else:427            return valid_values[0]428    else:429        return True430 431 432def validate_sid(input_value):433    """434        SID= "S-1-" IdentifierAuthority 1*SubAuthority435               IdentifierAuthority= IdentifierAuthorityDec / IdentifierAuthorityHex436                  ; If the identifier authority is < 2^32, the437                  ; identifier authority is represented as a decimal438                  ; number439                  ; If the identifier authority is >= 2^32,440                  ; the identifier authority is represented in441                  ; hexadecimal442                IdentifierAuthorityDec =  1*10DIGIT443                  ; IdentifierAuthorityDec, top level authority of a444                  ; security identifier is represented as a decimal number445                IdentifierAuthorityHex = "0x" 12HEXDIG446                  ; IdentifierAuthorityHex, the top-level authority of a447                  ; security identifier is represented as a hexadecimal number448                SubAuthority= "-" 1*10DIGIT449                  ; Sub-Authority is always represented as a decimal number450                  ; No leading "0" characters are allowed when IdentifierAuthority451                  ; or SubAuthority is represented as a decimal number452                  ; All hexadecimal digits must be output in string format,453                  ; pre-pended by "0x"454 455        Revision (1 byte): An 8-bit unsigned integer that specifies the revision level of the SID. This value MUST be set to 0x01.456        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.457        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.458        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.459 460        If you have a SID like S-a-b-c-d-e-f-g-...461 462        Then the bytes are463        a 	(revision)464        N 	(number of dashes minus two)465        bbbbbb 	(six bytes of "b" treated as a 48-bit number in big-endian format)466        cccc 	(four bytes of "c" treated as a 32-bit number in little-endian format)467        dddd 	(four bytes of "d" treated as a 32-bit number in little-endian format)468        eeee 	(four bytes of "e" treated as a 32-bit number in little-endian format)469        ffff 	(four bytes of "f" treated as a 32-bit number in little-endian format)470 471    """472    if not isinstance(input_value, SEQUENCE_TYPES):473        sequence = False474        input_value = [input_value]475    else:476        sequence = True  # indicates if a sequence must be returned477 478    valid_values = []479    changed = False480    for element in input_value:481        if isinstance(element, STRING_TYPES):482            if element.startswith('S-'):483                parts = element.split('-')484                sid_bytes = pack('<q', int(parts[1]))[0:1]  # revision number485                sid_bytes += pack('<q', len(parts[3:]))[0:1]  # number of sub authorities486                if len(parts[2]) <= 10:487                    sid_bytes += pack('>q', int(parts[2]))[2:]  # authority (in dec)488                else:489                    sid_bytes += pack('>q', int(parts[2], 16))[2:]  # authority (in hex)490                for sub_auth in parts[3:]:491                    sid_bytes += pack('<q', int(sub_auth))[0:4]  # sub-authorities492                valid_values.append(sid_bytes)493                changed = True494 495    if changed:496        # valid_values = [check_backslash(value) for value in valid_values]497        if sequence:498            return valid_values499        else:500            return valid_values[0]501    else:502        return True503 
codekingpro/portable-devtools · Team Ai