@@ -3,11 +3,10 @@ use axum::Json;
33use axum_extra:: json;
44use axum_extra:: response:: ErasedJson ;
55use diesel:: QueryResult ;
6- use diesel_async:: async_connection_wrapper :: AsyncConnectionWrapper ;
6+ use diesel_async:: AsyncPgConnection ;
77use http:: request:: Parts ;
88use oauth2:: reqwest:: http_client;
99use oauth2:: { AuthorizationCode , CsrfToken , Scope , TokenResponse } ;
10- use tokio:: runtime:: Handle ;
1110
1211use crate :: app:: AppState ;
1312use crate :: email:: Emails ;
@@ -16,7 +15,7 @@ use crate::middleware::session::SessionExtension;
1615use crate :: models:: { NewUser , User } ;
1716use crate :: schema:: users;
1817use crate :: tasks:: spawn_blocking;
19- use crate :: util:: diesel:: { is_read_only_error, Conn } ;
18+ use crate :: util:: diesel:: is_read_only_error;
2019use crate :: util:: errors:: { bad_request, server_error, AppResult } ;
2120use crate :: views:: EncodableMe ;
2221use crates_io_github:: GithubUser ;
@@ -90,54 +89,48 @@ pub async fn authorize(
9089 session : SessionExtension ,
9190 req : Parts ,
9291) -> AppResult < Json < EncodableMe > > {
92+ // Make sure that the state we just got matches the session state that we
93+ // should have issued earlier.
94+ let session_state = session. remove ( "github_oauth_state" ) . map ( CsrfToken :: new) ;
95+ if !session_state. is_some_and ( |state| query. state . secret ( ) == state. secret ( ) ) {
96+ return Err ( bad_request ( "invalid state parameter" ) ) ;
97+ }
98+
99+ // Fetch the access token from GitHub using the code we just got
93100 let app_clone = app. clone ( ) ;
94101 let request_log = req. request_log ( ) . clone ( ) ;
95-
96- let conn = app. db_write ( ) . await ?;
97- spawn_blocking ( move || {
98- let conn: & mut AsyncConnectionWrapper < _ > = & mut conn. into ( ) ;
99-
100- // Make sure that the state we just got matches the session state that we
101- // should have issued earlier.
102- let session_state = session. remove ( "github_oauth_state" ) . map ( CsrfToken :: new) ;
103- if !session_state. is_some_and ( |state| query. state . secret ( ) == state. secret ( ) ) {
104- return Err ( bad_request ( "invalid state parameter" ) ) ;
105- }
106-
107- // Fetch the access token from GitHub using the code we just got
108- let token = app
102+ let token = spawn_blocking ( move || {
103+ app_clone
109104 . github_oauth
110105 . exchange_code ( query. code )
111106 . request ( http_client)
112107 . map_err ( |err| {
113108 request_log. add ( "cause" , err) ;
114109 server_error ( "Error obtaining token" )
115- } ) ?;
110+ } )
111+ } )
112+ . await ?;
116113
117- let token = token. access_token ( ) ;
114+ let token = token. access_token ( ) ;
118115
119- // Fetch the user info from GitHub using the access token we just got and create a user record
120- let ghuser = Handle :: current ( ) . block_on ( app. github . current_user ( token) ) ?;
121- let user = save_user_to_database ( & ghuser, token. secret ( ) , & app. emails , conn) ?;
116+ // Fetch the user info from GitHub using the access token we just got and create a user record
117+ let ghuser = app. github . current_user ( token) . await ?;
122118
123- // Log in by setting a cookie and the middleware authentication
124- session . insert ( "user_id" . to_string ( ) , user . id . to_string ( ) ) ;
119+ let mut conn = app . db_write ( ) . await ? ;
120+ let user = save_user_to_database ( & ghuser , token . secret ( ) , & app . emails , & mut conn ) . await ? ;
125121
126- Ok ( ( ) )
127- } )
128- . await ?;
122+ // Log in by setting a cookie and the middleware authentication
123+ session. insert ( "user_id" . to_string ( ) , user. id . to_string ( ) ) ;
129124
130- super :: me:: me ( app_clone , req) . await
125+ super :: me:: me ( app , req) . await
131126}
132127
133- fn save_user_to_database (
128+ async fn save_user_to_database (
134129 user : & GithubUser ,
135130 access_token : & str ,
136131 emails : & Emails ,
137- conn : & mut impl Conn ,
132+ conn : & mut AsyncPgConnection ,
138133) -> AppResult < User > {
139- use diesel:: prelude:: * ;
140-
141134 let new_user = NewUser :: new (
142135 user. id ,
143136 & user. login ,
@@ -146,23 +139,30 @@ fn save_user_to_database(
146139 access_token,
147140 ) ;
148141
149- match new_user. create_or_update ( user. email . as_deref ( ) , emails, conn) {
142+ match new_user
143+ . create_or_update ( user. email . as_deref ( ) , emails, conn)
144+ . await
145+ {
150146 Ok ( user) => Ok ( user) ,
151147 Err ( error) if is_read_only_error ( & error) => {
152148 // If we're in read only mode, we can't update their details
153149 // just look for an existing user
154- find_user_by_gh_id ( conn, user. id ) ?. ok_or_else ( || error. into ( ) )
150+ find_user_by_gh_id ( conn, user. id )
151+ . await ?
152+ . ok_or_else ( || error. into ( ) )
155153 }
156154 Err ( error) => Err ( error. into ( ) ) ,
157155 }
158156}
159157
160- fn find_user_by_gh_id ( conn : & mut impl Conn , gh_id : i32 ) -> QueryResult < Option < User > > {
158+ async fn find_user_by_gh_id ( conn : & mut AsyncPgConnection , gh_id : i32 ) -> QueryResult < Option < User > > {
161159 use diesel:: prelude:: * ;
160+ use diesel_async:: RunQueryDsl ;
162161
163162 users:: table
164163 . filter ( users:: gh_id. eq ( gh_id) )
165164 . first ( conn)
165+ . await
166166 . optional ( )
167167}
168168
@@ -175,20 +175,24 @@ pub async fn logout(session: SessionExtension) -> Json<bool> {
175175#[ cfg( test) ]
176176mod tests {
177177 use super :: * ;
178- use crate :: test_util:: test_db_connection;
178+ use crates_io_test_db:: TestDatabase ;
179+ use diesel_async:: AsyncConnection ;
179180
180- #[ test]
181- fn gh_user_with_invalid_email_doesnt_fail ( ) {
181+ #[ tokio :: test]
182+ async fn gh_user_with_invalid_email_doesnt_fail ( ) {
182183 let emails = Emails :: new_in_memory ( ) ;
183- let ( _test_db, conn) = & mut test_db_connection ( ) ;
184+
185+ let test_db = TestDatabase :: new ( ) ;
186+ let mut conn = AsyncPgConnection :: establish ( test_db. url ( ) ) . await . unwrap ( ) ;
187+
184188 let gh_user = GithubUser {
185189 email : Some ( "String.Format(\" {0}.{1}@live.com\" , FirstName, LastName)" . into ( ) ) ,
186190 name : Some ( "My Name" . into ( ) ) ,
187191 login : "github_user" . into ( ) ,
188192 id : -1 ,
189193 avatar_url : None ,
190194 } ;
191- let result = save_user_to_database ( & gh_user, "arbitrary_token" , & emails, conn) ;
195+ let result = save_user_to_database ( & gh_user, "arbitrary_token" , & emails, & mut conn) . await ;
192196
193197 assert ! (
194198 result. is_ok( ) ,
0 commit comments