Skip to content

ref(token): Use secrecy crate to store auth token #2116

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

Merged
merged 2 commits into from
Aug 1, 2024
Merged
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
16 changes: 16 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ zip = "0.6.4"
data-encoding = "2.3.3"
magic_string = "0.3.4"
chrono-tz = "0.8.4"
secrecy = "0.8.0"

[dev-dependencies]
insta = { version = "1.26.0", features = ["redactions", "yaml"] }
Expand Down
6 changes: 5 additions & 1 deletion src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use lazy_static::lazy_static;
use log::{debug, info, warn};
use parking_lot::Mutex;
use regex::{Captures, Regex};
use secrecy::ExposeSecret;
use sentry::protocol::{Exception, Values};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -1687,7 +1688,10 @@ impl ApiRequest {
}
Auth::Token(ref token) => {
debug!("using token authentication");
self.with_header("Authorization", &format!("Bearer {token}"))
self.with_header(
"Authorization",
&format!("Bearer {}", token.raw().expose_secret()),
)
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ini::Ini;
use lazy_static::lazy_static;
use log::{debug, info, set_max_level, warn};
use parking_lot::Mutex;
use secrecy::ExposeSecret;
use sentry::types::Dsn;

use crate::constants::CONFIG_INI_FILE_PATH;
Expand Down Expand Up @@ -181,8 +182,11 @@ impl Config {
self.cached_base_url = token_url.to_string();
}

self.ini
.set_to(Some("auth"), "token".into(), val.to_string());
self.ini.set_to(
Some("auth"),
"token".into(),
val.raw().expose_secret().clone(),
);
}
Some(Auth::Key(ref val)) => {
self.ini
Expand Down
24 changes: 8 additions & 16 deletions src/utils/auth_token/auth_token_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use super::{AuthTokenPayload, ORG_AUTH_TOKEN_PREFIX, USER_TOKEN_PREFIX};
use super::{OrgAuthToken, UserAuthToken};
use std::fmt::{Display, Formatter, Result};
use secrecy::SecretString;

/// Represents a (soft) validated Sentry auth token.
#[derive(Debug, Clone)]
Expand All @@ -21,8 +21,8 @@ impl AuthToken {
}

/// Retrieves a reference to the auth token string.
fn as_str(&self) -> &str {
self.0.as_str()
pub fn raw(&self) -> &SecretString {
self.0.raw()
}

/// Returns whether the auth token follows a recognized format. If this function returns false,
Expand All @@ -48,14 +48,6 @@ impl From<&str> for AuthToken {
}
}

impl Display for AuthToken {
/// Displays the auth token string.
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{}", self.as_str())?;
Ok(())
}
}

/// Inner representation of AuthToken type, containing all possible auth token types.
#[derive(Debug, Clone)]
enum AuthTokenInner {
Expand All @@ -66,7 +58,7 @@ enum AuthTokenInner {
User(UserAuthToken),

/// Represents an auth token that has an unrecognized format.
Unknown(String),
Unknown(SecretString),
}

impl AuthTokenInner {
Expand All @@ -78,7 +70,7 @@ impl AuthTokenInner {
} else if let Ok(user_auth_token) = UserAuthToken::try_from(auth_string.clone()) {
AuthTokenInner::User(user_auth_token)
} else {
AuthTokenInner::Unknown(auth_string)
AuthTokenInner::Unknown(auth_string.into())
}
}

Expand All @@ -92,10 +84,10 @@ impl AuthTokenInner {
}

/// Retrieves a reference to the auth token string.
fn as_str(&self) -> &str {
fn raw(&self) -> &SecretString {
match self {
AuthTokenInner::Org(ref org_auth_token) => org_auth_token.as_str(),
AuthTokenInner::User(user_auth_token) => user_auth_token.as_str(),
AuthTokenInner::Org(ref org_auth_token) => org_auth_token.raw(),
AuthTokenInner::User(user_auth_token) => user_auth_token.raw(),
AuthTokenInner::Unknown(auth_string) => auth_string,
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/utils/auth_token/org_auth_token.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use super::{AuthTokenParseError, Result, ORG_AUTH_TOKEN_PREFIX};
use secrecy::SecretString;
use serde::{Deserialize, Deserializer};

const ORG_TOKEN_SECRET_BYTES: usize = 32;

/// Represents a valid org auth token.
#[derive(Debug, Clone)]
pub struct OrgAuthToken {
auth_string: String,
auth_string: SecretString,
pub payload: AuthTokenPayload,
}

Expand Down Expand Up @@ -75,14 +76,16 @@ impl OrgAuthToken {
return Err(AuthTokenParseError);
}

let auth_string = auth_string.into();

Ok(OrgAuthToken {
auth_string,
payload,
})
}

/// Retrieves a reference to the auth token string.
pub fn as_str(&self) -> &str {
pub fn raw(&self) -> &SecretString {
&self.auth_string
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/utils/auth_token/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use super::AuthToken;
use rstest::rstest;

use secrecy::ExposeSecret;
// Org auth token tests -----------------------------------------------------

#[test]
Expand All @@ -22,7 +22,7 @@ fn test_valid_org_auth_token() {
assert_eq!(payload.org, "sentry");
assert_eq!(payload.url, "http://localhost:8000");

assert_eq!(good_token, token.to_string());
assert_eq!(good_token, token.raw().expose_secret().clone());

assert!(token.format_recognized());
}
Expand All @@ -44,7 +44,7 @@ fn test_valid_org_auth_token_missing_url() {
assert_eq!(payload.org, "sentry");
assert!(payload.url.is_empty());

assert_eq!(good_token, token.to_string());
assert_eq!(good_token, token.raw().expose_secret().clone());

assert!(token.format_recognized());
}
Expand All @@ -60,7 +60,7 @@ fn test_valid_user_auth_token(#[case] token_str: &'static str) {
let token = AuthToken::from(good_token.clone());

assert!(token.payload().is_none());
assert_eq!(good_token, token.to_string());
assert_eq!(good_token, token.raw().expose_secret().clone());

assert!(token.format_recognized());
}
Expand Down Expand Up @@ -130,7 +130,7 @@ fn test_valid_user_auth_token(#[case] token_str: &'static str) {
fn test_unknown_auth_token(#[case] token_str: &'static str) {
let token = AuthToken::from(token_str.to_owned());

assert_eq!(token_str, token.to_string());
assert_eq!(token_str, token.raw().expose_secret().clone());
assert!(token.payload().is_none());

assert!(!token.format_recognized());
Expand Down
7 changes: 4 additions & 3 deletions src/utils/auth_token/user_auth_token.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::{AuthTokenParseError, Result, USER_TOKEN_PREFIX};
use secrecy::SecretString;

const USER_TOKEN_BYTES: usize = 32;

/// Represents a valid User Auth Token.
#[derive(Debug, Clone)]
pub struct UserAuthToken(String);
pub struct UserAuthToken(SecretString);

impl UserAuthToken {
/// Constructs a new UserAuthToken from a string. Returns an error if the string is not a valid user auth token.
Expand All @@ -16,14 +17,14 @@ impl UserAuthToken {
let bytes = data_encoding::HEXLOWER_PERMISSIVE.decode(secret_portion.as_bytes());

if bytes.is_ok() && bytes.unwrap().len() == USER_TOKEN_BYTES {
Ok(UserAuthToken(auth_string))
Ok(UserAuthToken(auth_string.into()))
} else {
Err(AuthTokenParseError)
}
}

/// Retrieves a reference to the auth token string.
pub fn as_str(&self) -> &str {
pub fn raw(&self) -> &SecretString {
&self.0
}
}
Expand Down
9 changes: 9 additions & 0 deletions tests/integration/_cases/token-redacted.trycmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
```
$ sentry-cli sourcemaps upload --auth-token not-following-token-format -o asdf -p sntrys_project_looks_like_token ./ --log-level=info
? failed
[..]
[..]
[..]INFO[..] sentry-cli was invoked with the following command line: "[..]" "sourcemaps" "upload" "--auth-token" (redacted) "-o" "asdf" "-p" (redacted) "./" "--log-level=info"
...

```
5 changes: 5 additions & 0 deletions tests/integration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,8 @@ pub fn assert_endpoints(mocks: &[Mock]) {
mock.assert();
}
}

#[test]
pub fn token_redacted() {
register_test("token-redacted.trycmd");
}
Loading