okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
4006092
main
1%% The Google account refresh and Colab account choice against a one-shot local token endpoint. 2- module ( albedo_google_oauth_test_support ). 3- export ([ refresh / 2 , signed_in / 1 ]). 4 5- define ( APP , # { name =><< "Google Colab" >>, client_id =><< "id" >>, client_secret =><< "secret" >>, 6key =><< "google-colab" >>, required =>[<< "access" >>, << "refresh" >>]}). 7 8%% Refreshes the first account of AccountsJson against a server answering Status 9%% and Body: {Result, creds.json after, the form the server received}. 10refresh ( AccountsJson , { Status , Body }) -> 11Home = home ( AccountsJson ), 12{ Url , Await } = serve ( Status , Body ), 13Path = albedo_credentials : creds_path ( Home ), 14{ ok , # {<< "google-colab" >> :=[ First | _ ]}} = albedo_credentials : accounts ( Path ), 15Result = case albedo_google_oauth : ensure ( maps : put ( token_url , Url , ?APP ), Path , First ) of 16{ ok , # {<< "access" >> :=Access }} -> { ok , Access }; 17{ error , invalid } -> { error , << "invalid" >>}; 18{ error , _ } = Error -> Error 19end , 20{ Result , file_text ( Path ), Await ()}. 21 22signed_in ( AccountsJson ) -> 23albedo_colab_auth : signed_in ( home ( AccountsJson )). 24 25home ( AccountsJson ) -> 26Home = filename : join ( os : getenv ( "TMPDIR" , "/tmp" ), "albedo-google-" ++ integer_to_list ( erlang : unique_integer ([ positive ]))), 27ok = filelib : ensure_path ( Home ), 28ok = file : write_file ( filename : join ( Home , "creds.json" ), 29json : encode ( # {<< "accounts" >> =># {<< "google-colab" >> =>json : decode ( AccountsJson )}})), 30Home . 31 32file_text ( Path ) -> 33{ ok , Text } = file : read_file ( Path ), 34Text . 35 36%% A one-shot token endpoint. The request reaches the parent before the 37%% answer goes out, so by the time the client has its answer `Await` already 38%% holds it; closing the listener ends a server nobody called. 39serve ( Status , Body ) -> 40{ ok , Listen } = gen_tcp : listen ( 0 , [ binary , { active , false }, { packet , http_bin }, { ip , { 127 , 0 , 0 , 1 }}]), 41{ ok , { _ , Port }} = inet : sockname ( Listen ), 42Parent = self (), 43Ref = make_ref (), 44spawn ( fun () -> 45case gen_tcp : accept ( Listen ) of 46{ ok , Socket } -> 47{ ok , { http_request , _ , _ , _ }} = gen_tcp : recv ( Socket , 0 , 30000 ), 48Length = headers ( Socket , 0 ), 49ok = inet : setopts ( Socket , [{ packet , raw }]), 50{ ok , Form } = gen_tcp : recv ( Socket , Length , 30000 ), 51Parent ! { Ref , Form }, 52ok = gen_tcp : send ( Socket , [ "HTTP/1.1 " , integer_to_list ( Status ), " X\r\nContent-Type: application/json\r\n" , 53"Connection: close\r\nContent-Length: " , integer_to_list ( byte_size ( Body )), "\r\n\r\n" , Body ]), 54gen_tcp : close ( Socket ); 55{ error , _ } -> ok 56end 57end ), 58Url = "http://127.0.0.1:" ++ integer_to_list ( Port ) ++ "/token" , 59{ Url , fun () -> 60ok = gen_tcp : close ( Listen ), 61receive { Ref , Form } -> Form after 0 -> << "no request" >> end 62end }. 63 64headers ( Socket , Length ) -> 65case gen_tcp : recv ( Socket , 0 , 30000 ) of 66{ ok , http_eoh } -> Length ; 67{ ok , { http_header , _ , 'Content-Length' , _ , Value }} -> headers ( Socket , binary_to_integer ( Value )); 68{ ok , { http_header , _ , _ , _ , _ }} -> headers ( Socket , Length ) 69end .