44
55import httpx
66from flask import Flask , request , make_response
7- from saic_ismart_client_ng .net .crypto import decrypt_request , encrypt_response
7+ from saic_ismart_client_ng .net .crypto import decrypt_request , encrypt_response , encrypt_response_full
88from saic_ismart_client_ng .net .httpx import encrypt_httpx_request , decrypt_httpx_response
99
1010app = Flask (__name__ )
@@ -25,92 +25,122 @@ async def encrypt_httpx_request_wrapper(
2525 )
2626
2727
28+ async def passtrough (path ):
29+ passtrough_client = httpx .AsyncClient ()
30+ headers = dict (request .headers )
31+ params = request .args
32+ if request .method in ['POST' , 'PUT' ]:
33+ content = request .get_data (parse_form_data = False ).decode ('utf-8' )
34+ else :
35+ content = None
36+ headers .pop ('Host' )
37+ api_response = await passtrough_client .request (
38+ url = f'{ base_uri } { path } ' ,
39+ method = request .method ,
40+ params = params ,
41+ headers = headers ,
42+ data = content ,
43+ )
44+ api_response_headers = api_response .headers
45+ api_response_content = api_response .content
46+ api_response_code = api_response .status_code
47+ response = make_response (api_response_content )
48+ response .headers .update (api_response_headers .items ())
49+ return response , api_response_code
50+
51+
2852@app .route ('/api.app/v1/' , defaults = {'path' : '' })
2953@app .route ('/api.app/v1/<path:path>' , methods = ['GET' ])
30- def do_get (path ):
31- print (request )
32- return 'You want GET path: %s' % path
54+ async def do_get (path ):
55+ print (f"do_get { path } " )
56+ return await passtrough ( path )
3357
3458
3559@app .route ('/api.app/v1/' , defaults = {'path' : '' }, methods = ['POST' ])
3660@app .route ('/api.app/v1/<path:path>' , methods = ['POST' ])
3761async def do_post (path ):
62+ print (f"do_post { path } " )
3863 if path == 'oauth/token' :
39- raw_data = request .get_data (parse_form_data = False ).decode ('utf-8' )
40- decrypted = decrypt_request (
41- original_request_url = request .url ,
42- original_request_headers = request .headers ,
43- original_request_content = raw_data ,
44- base_uri = request .url .removesuffix (path ),
64+ return await handle_login (path )
65+ else :
66+ return await passtrough (path )
67+
68+
69+ async def handle_login (path ):
70+ decrypting_client = httpx .AsyncClient (
71+ event_hooks = {
72+ "request" : [encrypt_httpx_request_wrapper ],
73+ "response" : [decrypt_httpx_response ]
74+ },
75+ )
76+ raw_data = request .get_data (parse_form_data = False ).decode ('utf-8' )
77+ decrypted = decrypt_request (
78+ original_request_url = request .url ,
79+ original_request_headers = request .headers ,
80+ original_request_content = raw_data ,
81+ base_uri = request .url .removesuffix (path ),
82+ )
83+ unquoted = urllib .parse .parse_qs (decrypted )
84+ username = unquoted [b'username' ][0 ].decode ('utf-8' )
85+ password = unquoted [b'password' ][0 ].decode ('utf-8' )
86+ device_id = unquoted [b'deviceId' ][0 ].decode ('utf-8' )
87+ device_type = unquoted [b'deviceType' ][0 ].decode ('utf-8' )
88+ scope = unquoted [b'scope' ][0 ].decode ('utf-8' )
89+ grant_type = unquoted [b'grant_type' ][0 ].decode ('utf-8' )
90+ login_type = unquoted [b'loginType' ][0 ].decode ('utf-8' )
91+ country_code = unquoted [b'countryCode' ][0 ].decode ('utf-8' ) if b'countryCode' in unquoted else ''
92+ headers = {
93+ "Content-Type" : "application/x-www-form-urlencoded" ,
94+ "Accept" : "application/json" ,
95+ "Authorization" : request .headers ['Authorization' ]
96+ }
97+ form_body = {
98+ "grant_type" : grant_type ,
99+ "username" : username ,
100+ "password" : password ,
101+ "scope" : scope ,
102+ "deviceId" : device_id ,
103+ "deviceType" : device_type ,
104+ "loginType" : login_type ,
105+ "countryCode" : country_code
106+ }
107+ api_response = await decrypting_client .post (url = f'{ base_uri } { path } ' , data = form_body , headers = headers )
108+ if api_response .is_success :
109+ cached_tokens [username ] = api_response .text
110+ ts = api_response .headers ['app-send-date' ]
111+ new_content , new_headers = encrypt_response_full (
112+ original_request_url = str (api_response .url ),
113+ original_response_headers = api_response .headers ,
114+ original_response_content = api_response .text ,
115+ response_timestamp_ms = ts ,
116+ base_uri = base_uri ,
117+ tenant_id = '459771' ,
118+ user_token = ''
45119 )
46- unquoted = urllib .parse .parse_qs (decrypted )
47- username = unquoted [b'username' ][0 ].decode ('utf-8' )
48- if username in cached_tokens :
49- response = cached_tokens [username ]
50- else :
51- password = unquoted [b'password' ][0 ].decode ('utf-8' )
52- login_type = unquoted [b'loginType' ][0 ].decode ('utf-8' )
53- country_code = unquoted [b'countryCode' ][0 ].decode ('utf-8' ) if b'countryCode' in unquoted else ''
54- headers = {
55- "Content-Type" : "application/x-www-form-urlencoded" ,
56- "Accept" : "application/json" ,
57- "Authorization" : request .headers ['Authorization' ]
58- }
59- firebase_device_id = "cqSHOMG1SmK4k-fzAeK6hr:APA91bGtGihOG5SEQ9hPx3Dtr9o9mQguNiKZrQzboa-1C_UBlRZYdFcMmdfLvh9Q_xA8A0dGFIjkMhZbdIXOYnKfHCeWafAfLXOrxBS3N18T4Slr-x9qpV6FHLMhE9s7I6s89k9lU7DD"
60- form_body = {
61- "grant_type" : "password" ,
62- "username" : username ,
63- "password" : password ,
64- "scope" : "all" ,
65- "deviceId" : f"{ firebase_device_id } ###europecar" ,
66- "deviceType" : "1" , # 2 for huawei
67- "loginType" : login_type ,
68- "countryCode" : country_code
69- }
70- client = httpx .AsyncClient (
71- event_hooks = {
72- "request" : [encrypt_httpx_request_wrapper ],
73- "response" : [decrypt_httpx_response ]
74- },
75- )
76- response = await client .post (url = f'{ base_uri } { path } ' , data = form_body , headers = headers )
77- if response .is_success :
78- response_json = response .json ()
79- cached_tokens [username ] = response_json
80- text_content = json .dumps (response_json )
81- ts = response .headers ['app-send-date' ]
82- their_verification = response .headers ['app-verification-string' ]
83- new_content , new_headers = encrypt_response (
84- original_request_url = str (response .url ),
85- original_response_headers = response .headers ,
86- original_response_content = response .text ,
87- response_timestamp_ms = ts ,
88- base_uri = base_uri ,
89- tenant_id = '459771' ,
90- user_token = ''
91- )
92- response = make_response (new_content )
93- response .headers .update (new_headers )
94- return response
95- else :
96- return (response .status_code , response .content )
97-
98- return 'You want POST path: %s' % path
120+ response = make_response (new_content )
121+ generated_headers = dict (new_headers .items ())
122+ generated_headers .pop ('content-length' )
123+ response .headers .update (generated_headers )
124+ return response , api_response .status_code
125+ else :
126+ response = make_response (api_response .text )
127+ response .headers .update (api_response .headers .items ())
128+ return response , api_response .status_code
99129
100130
101131@app .route ('/api.app/v1/' , defaults = {'path' : '' }, methods = ['DELETE' ])
102132@app .route ('/api.app/v1/<path:path>' , methods = ['DELETE' ])
103- def do_delete (path ):
104- print (request )
105- return 'You want GET path: %s' % path
133+ async def do_delete (path ):
134+ print (f"do_delete { path } " )
135+ return await passtrough ( path )
106136
107137
108138@app .route ('/api.app/v1/' , defaults = {'path' : '' }, methods = ['PUT' ])
109139@app .route ('/api.app/v1/<path:path>' , methods = ['PUT' ])
110- def do_put (path ):
111- print (request )
112- return 'You want POST path: %s' % path
140+ async def do_put (path ):
141+ print (f"do_put { path } " )
142+ return await passtrough ( path )
113143
114144
115145if __name__ == '__main__' :
116- app .run ()
146+ app .run (host = "127.0.0.1" , port = 8080 , debug = True )
0 commit comments