Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove async_trait dependency #932

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions Cargo.lock.msrv

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 31 additions & 23 deletions docs/source/connecting/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,49 +32,57 @@ Finally, to make use of the custom authentication, use the `authenticator_provid
# extern crate scylla_cql;
# extern crate tokio;
# extern crate bytes;
# extern crate async_trait;
# use std::error::Error;
# use std::sync::Arc;
# use std::pin::Pin;
# use std::future::Future;
use bytes::{BufMut, BytesMut};
use async_trait::async_trait;
use scylla::authentication::{AuthError, AuthenticatorProvider, AuthenticatorSession};

struct CustomAuthenticator;

#[async_trait]
impl AuthenticatorSession for CustomAuthenticator {
// to handle an authentication challenge initiated by the server.
// The information contained in the token parameter is authentication protocol specific.
// It may be NULL or empty.
async fn evaluate_challenge(
&mut self,
_token: Option<&[u8]>,
) -> Result<Option<Vec<u8>>, AuthError> {
Err("Challenges are not expected".to_string())
fn evaluate_challenge<'a>(
&'a mut self,
token: Option<&'a [u8]>,
) -> Pin<Box<dyn Future<Output = Result<Option<Vec<u8>>, AuthError>> + Send + 'a>> {
Box::pin(async move {
Err("Challenges are not expected".to_string())
})
}

// to handle the success phase of exchange. The token parameters contain information that may be used to finalize the request.
async fn success(&mut self, _token: Option<&[u8]>) -> Result<(), AuthError> {
Ok(())
fn success<'a>(
&'a mut self,
token: Option<&'a [u8]>
) -> Pin<Box<dyn Future<Output = Result<(), AuthError>> + Send + 'a>> {
Box::pin(async move { Ok(()) })
}
}

struct CustomAuthenticatorProvider;

#[async_trait]
impl AuthenticatorProvider for CustomAuthenticatorProvider {
async fn start_authentication_session(
&self,
_name: &str,
) -> Result<(Option<Vec<u8>>, Box<dyn AuthenticatorSession>), AuthError> {
let mut response = BytesMut::new();
let cred = "\0cassandra\0cassandra";
let cred_length = 20;

response.put_i32(cred_length);
response.put_slice(cred.as_bytes());

Ok((Some(response.to_vec()), Box::new(CustomAuthenticator)))
fn start_authentication_session<'a>(
&'a self,
authenticator_name: &'a str,
) -> Pin<Box<dyn Future<Output = Result<(Option<Vec<u8>>, Box<dyn AuthenticatorSession>), AuthError>> + Send + 'a>> {
Box::pin(async move {
let mut response = BytesMut::new();
let cred = "\0cassandra\0cassandra";
let cred_length = 20;

response.put_i32(cred_length);
response.put_slice(cred.as_bytes());

Ok((
Some(response.to_vec()),
Box::new(CustomAuthenticator) as Box<dyn AuthenticatorSession>
))
})
}
}

Expand Down
1 change: 0 additions & 1 deletion scylla-cql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ num-bigint-04 = { package = "num-bigint", version = "0.4", optional = true }
bigdecimal-04 = { package = "bigdecimal", version = "0.4", optional = true }
chrono = { version = "0.4.27", default-features = false, optional = true }
lz4_flex = { version = "0.11.1" }
async-trait = "0.1.57"
serde = { version = "1.0", features = ["derive"], optional = true }
time = { version = "0.3", optional = true }

Expand Down
1 change: 0 additions & 1 deletion scylla/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ arc-swap = "1.3.0"
dashmap = "5.2"
lz4_flex = { version = "0.11.1" }
smallvec = "1.8.0"
async-trait = "0.1.56"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_yaml = { version = "0.9.14", optional = true }
url = { version = "2.3.1", optional = true }
Expand Down
80 changes: 44 additions & 36 deletions scylla/src/authentication/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
use async_trait::async_trait;
use bytes::{BufMut, BytesMut};

use crate::utils::futures::BoxedFuture;

/// Type to represent an authentication error message.
pub type AuthError = String;

/// Type to represent an initial auth response with an authenticator session.
pub(crate) type AuthInitialResponseAndSession = (Option<Vec<u8>>, Box<dyn AuthenticatorSession>);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another case of the same problem of pub(crate) in pub item.
How does it look in generated documentation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's the same as for BoxedFuture alias. There is no problem in the docs:
image

However, when I use the driver in some user application and generate the definition using vscode hints, the generated function signature contains the opaque type:

struct Foo;

impl AuthenticatorProvider for Foo {
    fn start_authentication_session<'a>(
        &'a self,
        authenticator_name: &'a str,
    ) -> scylla::BoxedFuture<
        '_,
        std::prelude::v1::Result<AuthInitialResponseAndSession, scylla::authentication::AuthError>,
    > {
        todo!()
    }
}

What's interesting tho, is that the compiler can deduce the type in the error message when the function is not implemented:

not all trait items implemented, missing: `start_authentication_session`
  --> src/main.rs:14:1
   |
14 | impl AuthenticatorProvider for Foo {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `start_authentication_session` in implementation
   |
   = help: implement the missing item: `fn start_authentication_session(&'a self, _: &'a str) -> Pin<Box<(dyn Future<Output = std::result::Result<(Option<Vec<u8>>, Box<(dyn AuthenticatorSession + 'static)>), String>> + Send + 'a)>> { todo!() }`

I'm not sure why vscode generates the function declaration with an opaque type.

I think that we should make this alias public as well. However, i think we should come up with a more fitting name for this type alias (or introduce a struct instead). I only introduced this alias because clippy was complaining about the complexity of the returned type and I wanted the alias to be used internally - unfortunately, it appears in the public trait declaration.

/// Trait used to represent a user-defined custom authentication.
#[async_trait]
pub trait AuthenticatorSession: Send + Sync {
/// To handle an authentication challenge initiated by the server.
/// The information contained in the token parameter is authentication protocol specific.
/// It may be NULL or empty.
async fn evaluate_challenge(
&mut self,
token: Option<&[u8]>,
) -> Result<Option<Vec<u8>>, AuthError>;
fn evaluate_challenge<'a>(
&'a mut self,
token: Option<&'a [u8]>,
) -> BoxedFuture<'_, Result<Option<Vec<u8>>, AuthError>>;

/// To handle the success phase of exchange.
/// The token parameters contain information that may be used to finalize the request.
async fn success(&mut self, token: Option<&[u8]>) -> Result<(), AuthError>;
fn success<'a>(&'a mut self, token: Option<&'a [u8]>)
-> BoxedFuture<'_, Result<(), AuthError>>;
}

/// Trait used to represent a factory of [`AuthenticatorSession`] instances.
Expand All @@ -27,29 +31,32 @@ pub trait AuthenticatorSession: Send + Sync {
///
/// Default: [`PlainTextAuthenticator`] is the default authenticator which requires username and
/// password. It can be set by using SessionBuilder::user(\"user\", \"pass\") method.
#[async_trait]
pub trait AuthenticatorProvider: Sync + Send {
/// A pair of initial response and boxed [`AuthenticatorSession`]
/// should be returned if authentication is required by the server.
async fn start_authentication_session(
&self,
authenticator_name: &str,
) -> Result<(Option<Vec<u8>>, Box<dyn AuthenticatorSession>), AuthError>;
fn start_authentication_session<'a>(
&'a self,
authenticator_name: &'a str,
) -> BoxedFuture<'_, Result<AuthInitialResponseAndSession, AuthError>>;
}

struct PlainTextAuthenticatorSession;

#[async_trait]
impl AuthenticatorSession for PlainTextAuthenticatorSession {
async fn evaluate_challenge(
&mut self,
_token: Option<&[u8]>,
) -> Result<Option<Vec<u8>>, AuthError> {
Err("Challenges are not expected during PlainTextAuthentication".to_string())
fn evaluate_challenge<'a>(
&'a mut self,
_token: Option<&'a [u8]>,
) -> BoxedFuture<'_, Result<Option<Vec<u8>>, AuthError>> {
Box::pin(async move {
Err("Challenges are not expected during PlainTextAuthentication".to_string())
})
}

async fn success(&mut self, _token: Option<&[u8]>) -> Result<(), AuthError> {
Ok(())
fn success<'a>(
&'a mut self,
_token: Option<&'a [u8]>,
) -> BoxedFuture<'_, Result<(), AuthError>> {
Box::pin(async move { Ok(()) })
}
}

Expand All @@ -66,24 +73,25 @@ impl PlainTextAuthenticator {
}
}

#[async_trait]
impl AuthenticatorProvider for PlainTextAuthenticator {
async fn start_authentication_session(
&self,
_authenticator_name: &str,
) -> Result<(Option<Vec<u8>>, Box<dyn AuthenticatorSession>), AuthError> {
let mut response = BytesMut::new();
let username_as_bytes = self.username.as_bytes();
let password_as_bytes = self.password.as_bytes();
fn start_authentication_session<'a>(
&'a self,
_authenticator_name: &'a str,
) -> BoxedFuture<'_, Result<AuthInitialResponseAndSession, AuthError>> {
Box::pin(async move {
let mut response = BytesMut::new();
let username_as_bytes = self.username.as_bytes();
let password_as_bytes = self.password.as_bytes();

response.put_u8(0);
response.put_slice(username_as_bytes);
response.put_u8(0);
response.put_slice(password_as_bytes);
response.put_u8(0);
response.put_slice(username_as_bytes);
response.put_u8(0);
response.put_slice(password_as_bytes);

Ok((
Some(response.to_vec()),
Box::new(PlainTextAuthenticatorSession),
))
Ok((
Some(response.to_vec()),
Box::new(PlainTextAuthenticatorSession) as Box<dyn AuthenticatorSession>,
))
})
}
}
2 changes: 2 additions & 0 deletions scylla/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ pub(crate) mod utils;
#[doc(hidden)]
pub use utils::test_utils;

pub use utils::futures::BoxedFuture;

pub use statement::batch;
pub use statement::prepared_statement;
pub use statement::query;
Expand Down
50 changes: 29 additions & 21 deletions scylla/src/transport/authenticate_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::authentication::{AuthError, AuthenticatorProvider, AuthenticatorSession};
use crate::authentication::{
AuthError, AuthInitialResponseAndSession, AuthenticatorProvider, AuthenticatorSession,
};
use crate::utils::futures::BoxedFuture;
use crate::utils::test_utils::unique_keyspace_name;
use async_trait::async_trait;
use bytes::{BufMut, BytesMut};
use std::sync::Arc;

Expand Down Expand Up @@ -28,34 +30,40 @@ async fn authenticate_superuser() {

struct CustomAuthenticator;

#[async_trait]
impl AuthenticatorSession for CustomAuthenticator {
async fn evaluate_challenge(
&mut self,
_token: Option<&[u8]>,
) -> Result<Option<Vec<u8>>, AuthError> {
Err("Challenges are not expected".to_string())
fn evaluate_challenge<'a>(
&'a mut self,
_token: Option<&'a [u8]>,
) -> BoxedFuture<'_, Result<Option<Vec<u8>>, AuthError>> {
Box::pin(async move { Err("Challenges are not expected".to_string()) })
}

async fn success(&mut self, _token: Option<&[u8]>) -> Result<(), AuthError> {
Ok(())
fn success<'a>(
&'a mut self,
_token: Option<&'a [u8]>,
) -> BoxedFuture<'_, Result<(), AuthError>> {
Box::pin(async move { Ok(()) })
}
}

struct CustomAuthenticatorProvider;

#[async_trait]
impl AuthenticatorProvider for CustomAuthenticatorProvider {
async fn start_authentication_session(
&self,
_authenticator_name: &str,
) -> Result<(Option<Vec<u8>>, Box<dyn AuthenticatorSession>), AuthError> {
let mut response = BytesMut::new();
let cred = "\0cassandra\0cassandra";

response.put_slice(cred.as_bytes());

Ok((Some(response.to_vec()), Box::new(CustomAuthenticator)))
fn start_authentication_session<'a>(
&'a self,
_authenticator_name: &'a str,
) -> BoxedFuture<'_, Result<AuthInitialResponseAndSession, AuthError>> {
Box::pin(async move {
let mut response = BytesMut::new();
let cred = "\0cassandra\0cassandra";

response.put_slice(cred.as_bytes());

Ok((
Some(response.to_vec()),
Box::new(CustomAuthenticator) as Box<dyn AuthenticatorSession>,
))
})
}
}

Expand Down
Loading
Loading