def encrypt(self, width, height, smart, fit_in, flip_horizontal, flip_vertical, halign, valign, crop_left, crop_top, crop_right, crop_bottom, image): url = "%s/%s" % (Url.generate_options(width, height, smart, False, fit_in, flip_horizontal, flip_vertical, halign, valign, crop_left, crop_top, crop_right, crop_bottom), hashlib.md5(image).hexdigest()) pad = lambda s: s + (16 - len(s) % 16) * "{" cipher = AES.new(self.salt) encrypted = base64.urlsafe_b64encode(cipher.encrypt(pad(url))) return encrypted
def test_can_generate_url(self): url = Url.generate_options( debug=True, width=300, height=200, smart=True, meta=True, trim=True, adaptive=True, full=True, fit_in=True, horizontal_flip=True, vertical_flip=True, halign='left', valign='top', crop_left=100, crop_top=100, crop_right=400, crop_bottom=400, filters='brightness(100)' ) expect(url).to_equal( 'debug/meta/trim/100x100:400x400/adaptive-full-fit-in/-300x-200/left/top/smart/filters:brightness(100)' )
def encrypt(self, width, height, smart, adaptive, full, fit_in, flip_horizontal, flip_vertical, halign, valign, trim, crop_left, crop_top, crop_right, crop_bottom, filters, image): generated_url = Url.generate_options(width=width, height=height, smart=smart, meta=False, adaptive=adaptive, full=full, fit_in=fit_in, horizontal_flip=flip_horizontal, vertical_flip=flip_vertical, halign=halign, valign=valign, trim=trim, crop_left=crop_left, crop_top=crop_top, crop_right=crop_right, crop_bottom=crop_bottom, filters=filters) url = "%s/%s" % (generated_url, hashlib.md5(image).hexdigest()) def pad(s): return s + (16 - len(s) % 16) * "{" cipher = AES.new(self.security_key, MODE_ECB) encrypted = base64.urlsafe_b64encode( cipher.encrypt(pad(url.encode('utf-8')))) return encrypted
def test_usage_new_format(): key = "my-security-key" image = "s.glbimg.com/et/bb/f/original/2011/03/24/VN0JiwzmOw0b0lg.jpg" thumbor_signer = Signer(key) thumbor_url = Url.generate_options(width=300, height=200, smart=True, adaptive=False, fit_in=False, horizontal_flip=False, vertical_flip=False, halign='center', valign='middle', crop_left=0, crop_top=0, crop_right=0, crop_bottom=0, filters=[]) thumbor_url = ('%s/%s' % (thumbor_url, image)).lstrip('/') thumbor_url = '/%s/%s' % (thumbor_signer.signature(thumbor_url), thumbor_url) crypto = CryptoURL(key=key) url = crypto.generate(width=300, height=200, smart=True, image_url=image) assert url == thumbor_url
def test_url_generate_min(): url = Url.generate_options( width=300, height=200 ) assert url == "300x200"
def test_url_generate_with_alignments(): url = Url.generate_options( halign='left', valign='top' ) assert url == "0x0/left/top", url
def test_usage_new_format(): key = "my-security-key" image = "s.glbimg.com/et/bb/f/original/2011/03/24/VN0JiwzmOw0b0lg.jpg" thumbor_signer = Signer(key) thumbor_url = Url.generate_options( width=300, height=200, smart=True, adaptive=False, fit_in=False, horizontal_flip=False, vertical_flip=False, halign='center', valign='middle', crop_left=0, crop_top=0, crop_right=0, crop_bottom=0, filters=[] ) thumbor_url = ('%s/%s' % (thumbor_url, image)).lstrip('/') thumbor_url = '/%s/%s' % (thumbor_signer.signature(thumbor_url), thumbor_url) crypto = CryptoURL(key=key) url = crypto.generate( width=300, height=200, smart=True, image_url=image ) assert url == thumbor_url
def test_url_generate_with_smart(): url = Url.generate_options( width=300, height=200, smart=True ) assert url == "300x200/smart"
def test_url_generate_with_flipping(): url = Url.generate_options(width=300, height=200, smart=True, horizontal_flip=True, vertical_flip=True) assert url == '-300x-200/smart'
def test_url_generate_with_manual_crop(): url = Url.generate_options(width=300, height=200, crop_left=10, crop_top=11, crop_right=12, crop_bottom=13) assert url == '10x11:12x13/300x200'
def test_url_generate_with_flipping(): url = Url.generate_options( width=300, height=200, smart=True, horizontal_flip=True, vertical_flip=True ) assert url == "-300x-200/smart"
def test_url_generate_with_manual_crop(): url = Url.generate_options( width=300, height=200, crop_left=10, crop_top=11, crop_right=12, crop_bottom=13 ) assert url == "10x11:12x13/300x200"
def test_complete_url(): url = Url.generate_options(width=300, height=200, smart=True, meta=True, horizontal_flip=True, vertical_flip=True, crop_left=10, crop_top=11, crop_right=12, crop_bottom=13) assert url == 'meta/10x11:12x13/-300x-200/smart', url
def encrypt(self, width, height, smart, fit_in, flip_horizontal, flip_vertical, halign, valign, crop_left, crop_top, crop_right, crop_bottom, image): url = "%s/%s" % (Url.generate_options( width, height, smart, False, fit_in, flip_horizontal, flip_vertical, halign, valign, crop_left, crop_top, crop_right, crop_bottom), hashlib.md5(image).hexdigest()) pad = lambda s: s + (16 - len(s) % 16) * "{" cipher = AES.new(self.salt) encrypted = base64.urlsafe_b64encode(cipher.encrypt(pad(url))) return encrypted
def test_complete_url(): url = Url.generate_options( width=300, height=200, smart=True, meta=True, horizontal_flip=True, vertical_flip=True, crop_left=10, crop_top=11, crop_right=12, crop_bottom=13 ) assert url == "meta/10x11:12x13/-300x-200/smart", url
def encrypt(self, width, height, smart, adaptive, full, fit_in, flip_horizontal, flip_vertical, halign, valign, trim, crop_left, crop_top, crop_right, crop_bottom, filters, image): generated_url = Url.generate_options( width=width, height=height, smart=smart, meta=False, adaptive=adaptive, full=full, fit_in=fit_in, horizontal_flip=flip_horizontal, vertical_flip=flip_vertical, halign=halign, valign=valign, trim=trim, crop_left=crop_left, crop_top=crop_top, crop_right=crop_right, crop_bottom=crop_bottom, filters=filters ) url = "%s/%s" % (generated_url, hashlib.md5(image).hexdigest()) def pad(s): return s + (16 - len(s) % 16) * "{" cipher = AES.new(self.security_key, MODE_ECB) encrypted = base64.urlsafe_b64encode(cipher.encrypt(pad(url.encode('utf-8')))) return encrypted
def encrypt( self, width, height, smart, adaptive, fit_in, flip_horizontal, flip_vertical, halign, valign, crop_left, crop_top, crop_right, crop_bottom, filters, image, ): url = "%s/%s" % ( Url.generate_options( width=width, height=height, smart=smart, meta=False, adaptive=adaptive, fit_in=fit_in, horizontal_flip=flip_horizontal, vertical_flip=flip_vertical, halign=halign, valign=valign, crop_left=crop_left, crop_top=crop_top, crop_right=crop_right, crop_bottom=crop_bottom, filters=filters, ), hashlib.md5(image).hexdigest(), ) pad = lambda s: s + (16 - len(s) % 16) * "{" cipher = AES.new(self.security_key) encrypted = base64.urlsafe_b64encode(cipher.encrypt(pad(url.encode("utf-8")))) return encrypted
def encrypt(self, width, height, smart, adaptive, fit_in, flip_horizontal, flip_vertical, halign, valign, crop_left, crop_top, crop_right, crop_bottom, filters, image): url = "%s/%s" % (Url.generate_options(width=width, height=height, smart=smart, meta=False, adaptive=adaptive, fit_in=fit_in, horizontal_flip=flip_horizontal, vertical_flip=flip_vertical, halign=halign, valign=valign, crop_left=crop_left, crop_top=crop_top, crop_right=crop_right, crop_bottom=crop_bottom, filters=filters), hashlib.md5(image).hexdigest()) pad = lambda s: s + (16 - len(s) % 16) * "{" cipher = AES.new(self.salt) encrypted = base64.urlsafe_b64encode(cipher.encrypt(pad(url.encode('utf-8')))) return encrypted
def test_can_generate_url(self): url = Url.generate_options(debug=True, width=300, height=200, smart=True, meta=True, trim=True, adaptive=True, full=True, fit_in=True, horizontal_flip=True, vertical_flip=True, halign='left', valign='top', crop_left=100, crop_top=100, crop_right=400, crop_bottom=400, filters='brightness(100)') expect(url).to_equal( 'debug/meta/trim/100x100:400x400/adaptive-full-fit-in/-300x-200/left/top/smart/filters:brightness(100)' )
def test_url_generate_min(): url = Url.generate_options(width=300, height=200) assert url == '300x200'
def test_default_url(): url = Url.generate_options() assert url == "0x0"
def test_url_generate_with_meta(): url = Url.generate_options( meta=True ) assert url == "meta/0x0"
def test_url_generate_with_smart(): url = Url.generate_options(width=300, height=200, smart=True) assert url == '300x200/smart'
def test_url_generate_with_alignments(): url = Url.generate_options(halign='left', valign='top') assert url == '0x0/left/top', url
def test_url_generate_with_meta(): url = Url.generate_options(meta=True) assert url == 'meta/0x0'
def test_can_generate_url_with_fitin(self): url = Url.generate_options(fit_in=True, adaptive=False, full=False) expect(url).to_equal('fit-in')
def test_can_generate_url_with_defaults(self): url = Url.generate_options() expect(url).to_be_empty()
def main(arguments=None): '''Converts a given url with the specified arguments.''' if arguments is None: arguments = sys.argv[1:] parser = optparse.OptionParser(usage='thumbor-url [options] imageurl or type thumbor-url -h (--help) for help', description=__doc__, version=__version__) parser.add_option('-l', '--key_file', dest='key_file', default=None, help = 'The file to read the security key from [default: %default].' ) parser.add_option('-k', '--key', dest='key', default=None, help = 'The security key to encrypt the url with [default: %default].' ) parser.add_option('-w', '--width', dest='width', type='int', default=0, help = 'The target width for the image [default: %default].' ) parser.add_option('-e', '--height', dest='height', type='int', default=0, help = 'The target height for the image [default: %default].' ) parser.add_option('-n', '--fitin', dest='fitin', action='store_true', default=False, help = 'Indicates that fit-in resizing should be performed.' ) parser.add_option('-m', '--meta', dest='meta', action='store_true', default=False, help = 'Indicates that meta information should be retrieved.' ) parser.add_option('', '--adaptive', action='store_true', dest='adaptive', default=False, help = 'Indicates that adaptive fit-in cropping should be used.' ) parser.add_option('-s', '--smart', action='store_true', dest='smart', default=False, help = 'Indicates that smart cropping should be used.' ) parser.add_option('-t', '--trim', action='store_true', default=False, help='Indicate that surrounding whitespace should be trimmed.') parser.add_option('-f', '--horizontal-flip', action='store_true', dest='horizontal_flip', default=False, help = 'Indicates that the image should be horizontally flipped.' ) parser.add_option('-v', '--vertical-flip', action='store_true', dest='vertical_flip', default=False, help = 'Indicates that the image should be vertically flipped.') parser.add_option('-a', '--halign', dest='halign', default='center', help = 'The horizontal alignment to use for cropping [default: %default].' ) parser.add_option('-i', '--valign', dest='valign', default='middle', help = 'The vertical alignment to use for cropping [default: %default].' ) parser.add_option('', '--filters', dest='filters', default='', help = 'Filters to be applied to the image, e.g. brightness(10) [default: %default].' ) parser.add_option('-o', '--old-format', dest='old', action='store_true', default=False, help = 'Indicates that thumbor should generate old-format urls [default: %default].' ) parser.add_option('-c', '--crop', dest='crop', default=None, help = 'The coordinates of the points to manual cropping in the format leftxtop:rightxbottom (100x200:400x500) [default: %default].' ) (parsed_options, arguments) = parser.parse_args(arguments) if not arguments: print 'Error: The image argument is mandatory. For more information type thumbor-url -h' return image_url = arguments[0] if image_url.startswith('/'): image_url = image_url[1:] try: config = Config.load(None) except: config = None if config: print print "USING CONFIGURATION FILE AT %s" % config.config_file print if not parsed_options.key and not config: print 'Error: The -k or --key argument is mandatory. For more information type thumbor-url -h' return if parsed_options.key_file: f = open(parsed_options.key_file) security_key = f.read().strip() f.close() else: security_key = config.SECURITY_KEY if not parsed_options.key else parsed_options.key crop_left = crop_top = crop_right = crop_bottom = 0 if parsed_options.crop: crops = parsed_options.crop.split(':') crop_left, crop_top = crops[0].split('x') crop_right, crop_bottom = crops[1].split('x') if parsed_options.old: crypt = Cryptor(security_key) opt = crypt.encrypt(parsed_options.width, parsed_options.height, parsed_options.smart, parsed_options.adaptive, parsed_options.fitin, parsed_options.horizontal_flip, parsed_options.vertical_flip, parsed_options.halign, parsed_options.valign, crop_left, crop_top, crop_right, crop_bottom, parsed_options.filters, image_url) url = '/%s/%s' % (opt, image_url) print 'Encrypted URL:' else: signer = Signer(security_key) url = Url.generate_options( width=parsed_options.width, height=parsed_options.height, smart=parsed_options.smart, meta=parsed_options.meta, adaptive=parsed_options.adaptive, fit_in=parsed_options.fitin, horizontal_flip=parsed_options.horizontal_flip, vertical_flip=parsed_options.vertical_flip, halign=parsed_options.halign, valign=parsed_options.valign, trim=parsed_options.trim, crop_left=crop_left, crop_top=crop_top, crop_right=crop_right, crop_bottom=crop_bottom, filters=parsed_options.filters ) url = '%s/%s' % (url, image_url) url = url.lstrip('/') signature = signer.signature(url) url = '/%s/%s' % (signature, url) print 'Signed URL:' print url
def topic(self): return Url.generate_options(**kw)
def validate_image_permutations(self, kw): security_key = None if self.context.config.STORES_CRYPTO_KEY_FOR_EACH_IMAGE: security_key = yield gen.maybe_future( self.context.modules.storage.get_crypto( self.context.request.image_url)) self.context.request = RequestParameters(**kw) if self.validate_url(self.request.path, security_key): return else: # From the kw args given, generate a URL options fragment args = kw.copy() del args['hash'] del args['image'] del args['unsafe'] args = dict((k, v) for k, v in args.iteritems() if v) url_options = Url.generate_options(**args) load_target = kw['image'] # Undo `:` decoding load_target_with_encoded_colon = load_target.replace(':', '%3A') unescaped_url = "/%s/%s/%s" % (kw['hash'], url_options, load_target_with_encoded_colon) if self.validate_url(unescaped_url, security_key): kw['image'] = unquote(load_target_with_encoded_colon) self.request.path = unescaped_url return # Undo collapsed slashes with encoded `:` collapsed_slash = RE_SINGLE_SLASH_ENCODED.match( load_target_with_encoded_colon) if collapsed_slash: load_target_with_encoded_colon = load_target_with_encoded_colon.replace( collapsed_slash.group(1), collapsed_slash.group(1) + "/") unescaped_url = "/%s/%s/%s" % (kw['hash'], url_options, load_target_with_encoded_colon) if self.validate_url(unescaped_url, security_key): kw['image'] = unquote(load_target_with_encoded_colon) self.request.path = unescaped_url return # Undo %3A -> %253A encoding (can have multiple 252525...) quoted_target = quote(load_target.encode('utf-8')) encoded_percentage = RE_ENCODED_PERCENTAGE.match(quoted_target) if encoded_percentage: fixed_target = quoted_target.replace( encoded_percentage.group(1), '%3A') fixed_url = "/%s/%s/%s" % (kw['hash'], url_options, fixed_target) if self.validate_url(fixed_url, security_key): kw['image'] = unquote(fixed_target) self.request.path = fixed_url return # Undo collapsed slashes collapsed_slash = RE_SINGLE_SLASH.match(load_target) if collapsed_slash: load_target = load_target.replace( collapsed_slash.group(1), collapsed_slash.group(1) + "/") unescaped_url = "/%s/%s/%s" % (kw['hash'], url_options, load_target) if self.validate_url(unescaped_url, security_key): kw['image'] = load_target self.request.path = unescaped_url return # Attempt to validate with unescaped quoting load_target = quote(load_target.encode('utf-8'), safe='') unescaped_url = "/%s/%s/%s" % (kw['hash'], url_options, load_target) if self.validate_url(unescaped_url, security_key): kw['image'] = unquote(load_target) self.request.path = unescaped_url return # Attempt to validate with unquoting load_target = unquote(kw['image']) unescaped_url = "/%s/%s/%s" % (kw['hash'], url_options, quote(load_target.encode('utf-8'), safe='')) if self.validate_url(unescaped_url, security_key): self.request.path = unescaped_url kw['image'] = load_target
def test_default_url(): url = Url.generate_options() assert url == '0x0'
def main(arguments=None): '''Converts a given url with the specified arguments.''' if arguments is None: arguments = sys.argv[1:] parser = optparse.OptionParser(usage='thumbor-url [options] imageurl or type thumbor-url -h (--help) for help', description=__doc__, version=__version__) parser.add_option('-l', '--key_file', dest='key_file', default=None, help='The file to read the security key from [default: %default].') parser.add_option('-k', '--key', dest='key', default=None, help='The security key to encrypt the url with [default: %default].') parser.add_option('-w', '--width', dest='width', type='int', default=0, help='The target width for the image [default: %default].') parser.add_option('-e', '--height', dest='height', type='int', default=0, help='The target height for the image [default: %default].') parser.add_option('-n', '--fitin', dest='fitin', action='store_true', default=False, help='Indicates that fit-in resizing should be performed.') parser.add_option('-m', '--meta', dest='meta', action='store_true', default=False, help='Indicates that meta information should be retrieved.') parser.add_option('', '--adaptive', action='store_true', dest='adaptive', default=False, help='Indicates that adaptive fit-in cropping should be used.') parser.add_option('', '--full', action='store_true', dest='full', default=False, help='Indicates that fit-full cropping should be used.') parser.add_option('-s', '--smart', action='store_true', dest='smart', default=False, help='Indicates that smart cropping should be used.') parser.add_option('-t', '--trim', action='store_true', default=False, help='Indicate that surrounding whitespace should be trimmed.') parser.add_option('-f', '--horizontal-flip', action='store_true', dest='horizontal_flip', default=False, help='Indicates that the image should be horizontally flipped.') parser.add_option('-v', '--vertical-flip', action='store_true', dest='vertical_flip', default=False, help='Indicates that the image should be vertically flipped.') parser.add_option('-a', '--halign', dest='halign', default='center', help='The horizontal alignment to use for cropping [default: %default].') parser.add_option('-i', '--valign', dest='valign', default='middle', help='The vertical alignment to use for cropping [default: %default].') parser.add_option('', '--filters', dest='filters', default='', help='Filters to be applied to the image, e.g. brightness(10) [default: %default].') parser.add_option('-o', '--old-format', dest='old', action='store_true', default=False, help='Indicates that thumbor should generate old-format urls [default: %default].') parser.add_option('-c', '--crop', dest='crop', default=None, help='The coordinates of the points to manual cropping in the format leftxtop:rightxbottom (100x200:400x500) [default: %default].') (parsed_options, arguments) = parser.parse_args(arguments) if not arguments: print 'Error: The image argument is mandatory. For more information type thumbor-url -h' return image_url = arguments[0] if image_url.startswith('/'): image_url = image_url[1:] try: config = Config.load(None) except: config = None if not parsed_options.key and not config: print 'Error: The -k or --key argument is mandatory. For more information type thumbor-url -h' return if parsed_options.key_file: f = open(parsed_options.key_file) security_key = f.read().strip() f.close() else: security_key = config.SECURITY_KEY if not parsed_options.key else parsed_options.key crop_left = crop_top = crop_right = crop_bottom = 0 if parsed_options.crop: crops = parsed_options.crop.split(':') crop_left, crop_top = crops[0].split('x') crop_right, crop_bottom = crops[1].split('x') if parsed_options.old: crypt = Cryptor(security_key) opt = crypt.encrypt(parsed_options.width, parsed_options.height, parsed_options.smart, parsed_options.adaptive, parsed_options.full, parsed_options.fitin, parsed_options.horizontal_flip, parsed_options.vertical_flip, parsed_options.halign, parsed_options.valign, parsed_options.trim, crop_left, crop_top, crop_right, crop_bottom, parsed_options.filters, image_url) url = '/%s/%s' % (opt, image_url) print 'Encrypted URL:' else: signer = Signer(security_key) url = Url.generate_options( width=parsed_options.width, height=parsed_options.height, smart=parsed_options.smart, meta=parsed_options.meta, adaptive=parsed_options.adaptive, full=parsed_options.full, fit_in=parsed_options.fitin, horizontal_flip=parsed_options.horizontal_flip, vertical_flip=parsed_options.vertical_flip, halign=parsed_options.halign, valign=parsed_options.valign, trim=parsed_options.trim, crop_left=crop_left, crop_top=crop_top, crop_right=crop_right, crop_bottom=crop_bottom, filters=parsed_options.filters ) url = '%s/%s' % (url, image_url) url = url.lstrip('/') signature = signer.signature(url) url = '/%s/%s' % (signature, url) print 'Signed URL:' print url return url