4444 help = "Webhook notification URL." )
4545@option ("-t" , "--ttl" , type = int , default = 3600 ,
4646 help = ("URL expiration duration in seconds. Only relevant if cloning "
47- "restricted assets without providing an auth_key in which case "
47+ "restricted assets. If you do not provide an auth_key, "
4848 "a private download URL is generated which may incur additional "
4949 "bandwidth costs." ))
5050def clone (target , force , overwrite , concurrent_workers , fields ,
@@ -64,16 +64,21 @@ def clone(target, force, overwrite, concurrent_workers, fields,
6464 "as source environment." )
6565 return False
6666
67- source_config = config_to_dict (cloudinary .config ())
68- auth_token = source_config .get ("auth_token" )
69- if auth_token and not validate_authtoken (auth_token ):
67+ auth_token = cloudinary .config ().auth_token
68+ if auth_token :
7069 # It is important to validate auth_token if provided as this prevents
7170 # customer from having to re-run the command as well as
7271 # saving Admin API calls and time.
73- return False
72+ try :
73+ cloudinary .utils .generate_auth_token (acl = "/image/*" )
74+ except Exception as e :
75+ logger .error (f"{ e } - auth_token validation failed. "
76+ "Please double-check your auth_token parameters." )
77+ return False
7478
7579 source_assets = search_assets (force , search_exp )
7680 if not source_assets :
81+ # End command if search_exp contains unsupported type(s)
7782 return False
7883
7984 upload_list = []
@@ -85,44 +90,21 @@ def clone(target, force, overwrite, concurrent_workers, fields,
8590 updated_options .update (config_to_dict (target_config ))
8691 upload_list .append ((asset_url , {** updated_options }))
8792
93+ source_cloud_name = cloudinary .config ().cloud_name
8894 if not upload_list :
89- logger .error (style (f'No assets found in { cloudinary .config ().cloud_name } ' , fg = "red" ))
95+ logger .error (style ('No asset(s) found in '
96+ f'{ source_cloud_name } ' , fg = "red" ))
9097 return False
9198
92- logger .info (style (f'Copying { len (upload_list )} asset(s) from { cloudinary .config ().cloud_name } to { target_config .cloud_name } ' , fg = "blue" ))
99+ logger .info (style (f'Copying { len (upload_list )} asset(s) from '
100+ f'{ source_cloud_name } to '
101+ f'{ target_config .cloud_name } ' , fg = "blue" ))
93102
94103 run_tasks_concurrently (upload_file , upload_list , concurrent_workers )
95104
96105 return True
97106
98107
99- def validate_authtoken (auth_token ):
100- duration = auth_token .get ('duration' )
101- if not duration :
102- logger .error ("Duration is required when using auth_token. Include "
103- "`auth_token[duration]=<duration>` in your config." )
104- return False
105- try :
106- duration = int (duration )
107- if duration < 0 :
108- logger .error ("Duration cannot be negative." )
109- return False
110- except (ValueError , TypeError ):
111- logger .error ("Duration must be an integer." )
112- return False
113- key = auth_token .get ('key' )
114- if not key :
115- logger .error ("No auth_token key found. Include "
116- "`auth_token[key]=<key>` in your config." )
117- return False
118- try :
119- _digest ("" , key )
120- except Exception as e :
121- logger .error (f"Auth token invalid: { e } ." )
122- return False
123- return True
124-
125-
126108def search_assets (force , search_exp ):
127109 # Prevent other unsupported types to prevent
128110 # avoidable errors during the upload process
@@ -143,7 +125,7 @@ def search_assets(force, search_exp):
143125 search_exp += " AND (type:upload OR type:private OR type:authenticated)"
144126 else :
145127 search_exp = "type:upload OR type:private OR type:authenticated"
146- print ( search_exp )
128+
147129 search = cloudinary .search .Search ().expression (search_exp )
148130 search .fields (['tags' , 'context' , 'access_control' ,
149131 'secure_url' , 'display_name' , 'format' ])
@@ -172,12 +154,14 @@ def process_metadata(res, overwrite, async_, notification_url,
172154 # Generate a time-limited URL for restricted assets
173155 # Use private url if no auth_token provided
174156 if auth_token :
157+ # Don't add format if asset is raw
175158 pub_id_format = (pub_id if reso_type == "raw"
176159 else f"{ pub_id } .{ file_format } " )
177160 asset_url = cloudinary .utils .cloudinary_url (
178161 pub_id_format ,
179162 type = del_type ,
180163 resource_type = reso_type ,
164+ auth_token = {"duration" : ttl },
181165 secure = True ,
182166 sign_url = True )
183167 else :
0 commit comments