pub(crate) async fn authenticate_stream(
        &self,
        conn: &mut Connection,
        credential: &Credential,
        server_api: Option<&ServerApi>,
        first_round: impl Into<Option<FirstRound>>,
    ) -> Result<()> {
        let FirstRound {
            client_first,
            server_first,
        } = match first_round.into() {
            Some(first_round) => first_round,
            None => self.send_client_first(conn, credential, server_api).await?,
        };

        let ClientAuthInfo {
            username,
            password,
            source,
        } = self.client_auth_info(credential)?;

        let server_first = ServerFirst::parse(server_first)?;
        server_first.validate(client_first.nonce.as_str())?;

        let cache_entry_key = CacheEntry {
            password: password.to_string(),
            salt: server_first.salt().to_vec(),
            i: server_first.i(),
            mechanism: self.clone(),
        };
        let (should_update_cache, salted_password) =
            match CREDENTIAL_CACHE.read().await.get(&cache_entry_key) {
                Some(pwd) => (false, pwd.clone()),
                None => (
                    true,
                    self.compute_salted_password(
                        username,
                        password,
                        server_first.i(),
                        server_first.salt(),
                    )?,
                ),
            };

        let client_final = ClientFinal::new(
            source,
            salted_password.as_slice(),
            &client_first,
            &server_first,
            self,
            server_api.cloned(),
        )?;

        let command = client_final.to_command();

        let server_final_response = conn.send_command(command, None).await?;
        let server_final = ServerFinal::parse(server_final_response.auth_response_body("SCRAM")?)?;
        server_final.validate(salted_password.as_slice(), &client_final, self)?;

        if !server_final.done {
            // Normal SCRAM implementations would cease here. The following round trip is MongoDB
            // implementation specific on server versions < 4.4 and just consists of a client no-op
            // followed by a server no-op with "done: true".
            let noop = SaslContinue::new(
                source.into(),
                server_final.conversation_id().clone(),
                Vec::new(),
                server_api.cloned(),
            );
            let command = noop.into_command();

            let server_noop_response = conn.send_command(command, None).await?;
            let server_noop_response_document: Document =
                server_noop_response.auth_response_body("SCRAM")?;

            if server_noop_response_document
                .get("conversationId")
                .map(|id| id == server_final.conversation_id())
                != Some(true)
            {
                return Err(Error::authentication_error(
                    "SCRAM",
                    "mismatched conversationId's",
                ));
            };

            if !server_noop_response_document
                .get_bool("done")
                .unwrap_or(false)
            {
                return Err(Error::authentication_error(
                    "SCRAM",
                    "authentication did not complete successfully",
                ));
            }
        }

        if should_update_cache {
            let mut cache = CREDENTIAL_CACHE.write().await;
            if let Entry::Vacant(entry) = cache.entry(cache_entry_key) {
                entry.insert(salted_password);
            }
        }

        Ok(())
    }
/// Perform SCRAM authentication for a given stream.
Perform SCRAM authentication for a given stream.
Perform SCRAM authentication for a given stream .
pub(crate) async fn authenticate_stream(
        &self,
        conn: &mut Connection,
        credential: &Credential,
        server_api: Option<&ServerApi>,
        first_round: impl Into<Option<FirstRound>>,
    ) -> Result<()> {
        let FirstRound {
            client_first,
            server_first,
        } = match first_round.into() {
            Some(first_round) => first_round,
            None => self.send_client_first(conn, credential, server_api).await?,
        };
        let ClientAuthInfo {
            username,
            password,
            source,
        } = self.client_auth_info(credential)?;
        let server_first = ServerFirst::parse(server_first)?;
        server_first.validate(client_first.nonce.as_str())?;
        let cache_entry_key = CacheEntry {
            password: password.to_string(),
            salt: server_first.salt().to_vec(),
            i: server_first.i(),
            mechanism: self.clone(),
        };
        let (should_update_cache, salted_password) =
            match CREDENTIAL_CACHE.read().await.get(&cache_entry_key) {
                Some(pwd) => (false, pwd.clone()),
                None => (
                    true,
                    self.compute_salted_password(
                        username,
                        password,
                        server_first.i(),
                        server_first.salt(),
                    )?,
                ),
            };
        let client_final = ClientFinal::new(
            source,
            salted_password.as_slice(),
            &client_first,
            &server_first,
            self,
            server_api.cloned(),
        )?;
        let command = client_final.to_command();
        let server_final_response = conn.send_command(command, None).await?;
        let server_final = ServerFinal::parse(server_final_response.auth_response_body("SCRAM")?)?;
        server_final.validate(salted_password.as_slice(), &client_final, self)?;
        if !server_final.done {
            // Normal SCRAM implementations would cease here. The following round trip is MongoDB
            // implementation specific on server versions < 4.4 and just consists of a client no-op
            // followed by a server no-op with "done: true".
            let noop = SaslContinue::new(
                source.into(),
                server_final.conversation_id().clone(),
                Vec::new(),
                server_api.cloned(),
            );
            let command = noop.into_command();
            let server_noop_response = conn.send_command(command, None).await?;
            let server_noop_response_document: Document =
                server_noop_response.auth_response_body("SCRAM")?;
            if server_noop_response_document
                .get("conversationId")
                .map(|id| id == server_final.conversation_id())
                != Some(true)
            {
                return Err(Error::authentication_error(
                    "SCRAM",
                    "mismatched conversationId's",
                ));
            };
            if !server_noop_response_document
                .get_bool("done")
                .unwrap_or(false)
            {
                return Err(Error::authentication_error(
                    "SCRAM",
                    "authentication did not complete successfully",
                ));
            }
        }
        if should_update_cache {
            let mut cache = CREDENTIAL_CACHE.write().await;
            if let Entry::Vacant(entry) = cache.entry(cache_entry_key) {
                entry.insert(salted_password);
            }
        }
        Ok(())
    }
pub ( crate ) async fn authenticate_stream ( & self , conn : & mut Connection , credential : & Credential , server_api : Option < & ServerApi > , first_round : impl Into < Option < FirstRound > > , ) -> Result < ( ) > { let FirstRound { client_first , server_first , } = match first_round . into ( ) { Some ( first_round ) => first_round , None => self . send_client_first ( conn , credential , server_api ) . await ? , } ; let ClientAuthInfo { username , password , source , } = self . client_auth_info ( credential ) ? ; let server_first = ServerFirst :: parse ( server_first ) ? ; server_first . validate ( client_first . nonce . as_str ( ) ) ? ; let cache_entry_key = CacheEntry { password : password . to_string ( ) , salt : server_first . salt ( ) . to_vec ( ) , i : server_first . i ( ) , mechanism : self . clone ( ) , } ; let ( should_update_cache , salted_password ) = match CREDENTIAL_CACHE . read ( ) . await . get ( & cache_entry_key ) { Some ( pwd ) => ( false , pwd . clone ( ) ) , None => ( true , self . compute_salted_password ( username , password , server_first . i ( ) , server_first . salt ( ) , ) ? , ) , } ; let client_final = ClientFinal :: new ( source , salted_password . as_slice ( ) , & client_first , & server_first , self , server_api . cloned ( ) , ) ? ; let command = client_final . to_command ( ) ; let server_final_response = conn . send_command ( command , None ) . await ? ; let server_final = ServerFinal :: parse ( server_final_response . auth_response_body ( " " ) ? ) ? ; server_final . validate ( salted_password . as_slice ( ) , & client_final , self ) ? ; if ! server_final . done { // Normal SCRAM implementations would cease here. The following round trip is MongoDB // implementation specific on server versions < 4.4 and just consists of a client no-op // followed by a server no-op with "done: true". let noop = SaslContinue :: new ( source . into ( ) , server_final . conversation_id ( ) . clone ( ) , Vec :: new ( ) , server_api . cloned ( ) , ) ; let command = noop . into_command ( ) ; let server_noop_response = conn . send_command ( command , None ) . await ? ; let server_noop_response_document : Document = server_noop_response . auth_response_body ( " " ) ? ; if server_noop_response_document . get ( " " ) . map ( | id | id == server_final . conversation_id ( ) ) != Some ( true ) { return Err ( Error :: authentication_error ( " " , " " , ) ) ; } ; if ! server_noop_response_document . get_bool ( " " ) . unwrap_or ( false ) { return Err ( Error :: authentication_error ( " " , " " , ) ) ; } } if should_update_cache { let mut cache = CREDENTIAL_CACHE . write ( ) . await ; if let Entry :: Vacant ( entry ) = cache . entry ( cache_entry_key ) { entry . insert ( salted_password ) ; } } Ok ( ( ) ) }
Perform SCRAM authentication for a given stream.
Perform SCRAM authentication for a given stream .
&self
conn
&mut Connection
credential
&Credential
server_api
Option<&ServerApi>
first_round
impl Into<Option<FirstRound>>
&self
conn
&mut Connection
credential
&Credential
server_api
Option<&ServerApi>
first_round
impl Into<Option<FirstRound>>