-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Zoe Spellman
committed
Feb 21, 2024
1 parent
f9598ed
commit 09dfd5e
Showing
8 changed files
with
131 additions
and
332 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
use cucumber::{given, then, when, World}; | ||
|
||
use helpers::client::*; | ||
use ratings::utils::{Config, Infrastructure}; | ||
use sqlx::Row; | ||
use tonic::{Code, Status}; | ||
|
||
mod helpers; | ||
|
||
#[derive(Clone, Debug, Default, World)] | ||
struct AuthenticationWorld { | ||
client_hash: String, | ||
client: Option<TestClient>, | ||
tokens: Vec<String>, | ||
auth_error: Option<Status>, | ||
} | ||
|
||
#[given(expr = "a valid client hash")] | ||
fn generate_hash(world: &mut AuthenticationWorld) { | ||
world.client_hash = helpers::data_faker::rnd_sha_256(); | ||
} | ||
|
||
#[given(expr = "a bad client with the hash {word}")] | ||
fn with_hash(world: &mut AuthenticationWorld, hash: String) { | ||
world.client_hash = hash; | ||
} | ||
|
||
#[when(expr = "the client attempts to authenticate")] | ||
#[when(expr = "that client authenticates a second time")] | ||
#[given(expr = "an authenticated client")] | ||
async fn authenticate(world: &mut AuthenticationWorld) { | ||
let config = Config::load().expect("Could not load config"); | ||
|
||
world.client = Some(TestClient::new(config.socket())); | ||
|
||
match world | ||
.client | ||
.as_ref() | ||
.unwrap() | ||
.authenticate(&world.client_hash) | ||
.await | ||
{ | ||
Ok(resp) => world.tokens.push(resp.into_inner().token), | ||
Err(err) => world.auth_error = Some(err), | ||
} | ||
} | ||
|
||
#[then(expr = "the authentication is rejected")] | ||
fn check_rejected(world: &mut AuthenticationWorld) { | ||
assert!(world.auth_error.is_some()); | ||
|
||
let err = world.auth_error.as_ref().unwrap(); | ||
|
||
assert_eq!(err.code(), Code::InvalidArgument); | ||
} | ||
|
||
#[then(expr = "the returned token is valid")] | ||
#[then(expr = "both tokens are valid")] | ||
fn verify_token(world: &mut AuthenticationWorld) { | ||
assert!( | ||
world.auth_error.is_none(), | ||
"needed clean exit, instead got status {:?}", | ||
world.auth_error | ||
); | ||
|
||
let config = Config::load().expect("Could not load config"); | ||
|
||
for token in world.tokens.iter() { | ||
helpers::assert::assert_token_is_valid(token, &config.jwt_secret); | ||
} | ||
} | ||
|
||
#[then(expr = "the hash is only in the database once")] | ||
async fn no_double_auth(world: &mut AuthenticationWorld) { | ||
// In other test scenarios we might do this when we init the world, but | ||
// given authentication only needs this once this is fine | ||
let config = Config::load().expect("Could not load config"); | ||
let infra = Infrastructure::new(&config) | ||
.await | ||
.expect("Could not init DB"); | ||
|
||
// User still registered | ||
let row = sqlx::query("SELECT COUNT(*) FROM users WHERE client_hash = $1") | ||
.bind(&world.client_hash) | ||
.fetch_one(&mut *infra.repository().await.expect("could not connect to DB")) | ||
.await | ||
.unwrap(); | ||
|
||
let count: i64 = row.try_get("count").expect("Failed to get count"); | ||
|
||
// Only appears in db once | ||
assert_eq!(count, 1); | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
AuthenticationWorld::cucumber() | ||
.repeat_skipped() | ||
.init_tracing() | ||
.run_and_exit("tests/features/user/authentication.feature") | ||
.await | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
Feature: User authentication | ||
|
||
Scenario: The Snap Store tries to authenticate | ||
Given a valid client hash | ||
When the client attempts to authenticate | ||
Then the returned token is valid | ||
|
||
Rule: Client hashes must be exactly 64 characters long | ||
Scenario Outline: Eggman tries to directly rate a Snap with an unofficial client with an improper hardcoded "hash" | ||
Given a bad client with the hash <hash> | ||
When the client attempts to authenticate | ||
Then the authentication is rejected | ||
|
||
Examples: | ||
| hash | | ||
| notarealhash | | ||
| abcdefghijkabcdefghijkabcdefghijkabcdefghijkabcdefghijkabcdefghijk | | ||
|
||
Scenario: Charmy's client authenticates twice | ||
Given a valid client hash | ||
Given an authenticated client | ||
When that client authenticates a second time | ||
Then both tokens are valid | ||
And the hash is only in the database once |
File renamed without changes.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.