use std::convert::Into;
use std::fmt::Error as FormatterError;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use url::Url;
macro_rules! new_type {
(
$(#[$attr:meta])*
$name:ident(
$(#[$type_attr:meta])*
$type:ty
)
) => {
new_type![
@new_type $(#[$attr])*,
$name(
$(#[$type_attr])*
$type
),
concat!(
"Create a new `",
stringify!($name),
"` to wrap the given `",
stringify!($type),
"`."
),
impl {}
];
};
(
$(#[$attr:meta])*
$name:ident(
$(#[$type_attr:meta])*
$type:ty
)
impl {
$($item:tt)*
}
) => {
new_type![
@new_type $(#[$attr])*,
$name(
$(#[$type_attr])*
$type
),
concat!(
"Create a new `",
stringify!($name),
"` to wrap the given `",
stringify!($type),
"`."
),
impl {
$($item)*
}
];
};
(
@new_type $(#[$attr:meta])*,
$name:ident(
$(#[$type_attr:meta])*
$type:ty
),
$new_doc:expr,
impl {
$($item:tt)*
}
) => {
$(#[$attr])*
#[derive(Clone, Debug, PartialEq)]
pub struct $name(
$(#[$type_attr])*
$type
);
impl $name {
$($item)*
#[doc = $new_doc]
pub fn new(s: $type) -> Self {
$name(s)
}
}
impl Deref for $name {
type Target = $type;
fn deref(&self) -> &$type {
&self.0
}
}
impl Into<$type> for $name {
fn into(self) -> $type {
self.0
}
}
}
}
macro_rules! new_secret_type {
(
$(#[$attr:meta])*
$name:ident($type:ty)
) => {
new_secret_type![
$(#[$attr])*
$name($type)
impl {}
];
};
(
$(#[$attr:meta])*
$name:ident($type:ty)
impl {
$($item:tt)*
}
) => {
new_secret_type![
$(#[$attr])*,
$name($type),
concat!(
"Create a new `",
stringify!($name),
"` to wrap the given `",
stringify!($type),
"`."
),
concat!("Get the secret contained within this `", stringify!($name), "`."),
impl {
$($item)*
}
];
};
(
$(#[$attr:meta])*,
$name:ident($type:ty),
$new_doc:expr,
$secret_doc:expr,
impl {
$($item:tt)*
}
) => {
$(
#[$attr]
)*
pub struct $name($type);
impl $name {
$($item)*
#[doc = $new_doc]
pub fn new(s: $type) -> Self {
$name(s)
}
#[doc = $secret_doc]
pub fn secret(&self) -> &$type { &self.0 }
}
impl Debug for $name {
fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
write!(f, concat!(stringify!($name), "([redacted])"))
}
}
};
}
macro_rules! new_url_type {
(
$(#[$attr:meta])*
$name:ident
) => {
new_url_type![
@new_type_pub $(#[$attr])*,
$name,
concat!("Create a new `", stringify!($name), "` from a `String` to wrap a URL."),
concat!("Create a new `", stringify!($name), "` from a `Url` to wrap a URL."),
concat!("Return this `", stringify!($name), "` as a parsed `Url`."),
impl {}
];
};
(
$(#[$attr:meta])*
$name:ident
impl {
$($item:tt)*
}
) => {
new_url_type![
@new_type_pub $(#[$attr])*,
$name,
concat!("Create a new `", stringify!($name), "` from a `String` to wrap a URL."),
concat!("Create a new `", stringify!($name), "` from a `Url` to wrap a URL."),
concat!("Return this `", stringify!($name), "` as a parsed `Url`."),
impl {
$($item)*
}
];
};
(
@new_type_pub $(#[$attr:meta])*,
$name:ident,
$new_doc:expr,
$from_url_doc:expr,
$url_doc:expr,
impl {
$($item:tt)*
}
) => {
$(#[$attr])*
#[derive(Clone)]
pub struct $name(Url, String);
impl $name {
#[doc = $new_doc]
pub fn new(url: String) -> Result<Self, ::url::ParseError> {
Ok($name(Url::parse(&url)?, url))
}
#[doc = $from_url_doc]
pub fn from_url(url: Url) -> Self {
let s = url.to_string();
Self(url, s)
}
#[doc = $url_doc]
pub fn url(&self) -> &Url {
return &self.0;
}
$($item)*
}
impl Deref for $name {
type Target = String;
fn deref(&self) -> &String {
&self.1
}
}
impl ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let mut debug_trait_builder = f.debug_tuple(stringify!($name));
debug_trait_builder.field(&self.1);
debug_trait_builder.finish()
}
}
impl<'de> ::serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
struct UrlVisitor;
impl<'de> ::serde::de::Visitor<'de> for UrlVisitor {
type Value = $name;
fn expecting(
&self,
formatter: &mut ::std::fmt::Formatter
) -> ::std::fmt::Result {
formatter.write_str(stringify!($name))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: ::serde::de::Error,
{
$name::new(v.to_string()).map_err(E::custom)
}
}
deserializer.deserialize_str(UrlVisitor {})
}
}
impl ::serde::Serialize for $name {
fn serialize<SE>(&self, serializer: SE) -> Result<SE::Ok, SE::Error>
where
SE: ::serde::Serializer,
{
serializer.serialize_str(&self.1)
}
}
impl ::std::hash::Hash for $name {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) -> () {
::std::hash::Hash::hash(&(self.1), state);
}
}
impl Ord for $name {
fn cmp(&self, other: &$name) -> ::std::cmp::Ordering {
self.1.cmp(&other.1)
}
}
impl PartialOrd for $name {
fn partial_cmp(&self, other: &$name) -> Option<::std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for $name {
fn eq(&self, other: &$name) -> bool {
self.1 == other.1
}
}
impl Eq for $name {}
};
}
new_type![
#[derive(Deserialize, Serialize, Eq, Hash)]
ClientId(String)
];
new_url_type![
AuthUrl
];
new_url_type![
TokenUrl
];
new_url_type![
RedirectUrl
];
new_type![
#[derive(Deserialize, Serialize, Eq, Hash)]
ResponseType(String)
];
new_type![
#[derive(Deserialize, Serialize, Eq, Hash)]
ResourceOwnerUsername(String)
];
new_type![
#[derive(Deserialize, Serialize, Eq, Hash)]
Scope(String)
];
impl AsRef<str> for Scope {
fn as_ref(&self) -> &str {
self
}
}
new_type![
#[derive(Deserialize, Serialize, Eq, Hash)]
PkceCodeChallengeMethod(String)
];
new_secret_type![
#[derive(Deserialize, Serialize)]
PkceCodeVerifier(String)
];
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct PkceCodeChallenge {
code_challenge: String,
code_challenge_method: PkceCodeChallengeMethod,
}
impl PkceCodeChallenge {
pub fn new_random_sha256() -> (Self, PkceCodeVerifier) {
Self::new_random_sha256_len(32)
}
pub fn new_random_sha256_len(num_bytes: u32) -> (Self, PkceCodeVerifier) {
assert!(num_bytes >= 32 && num_bytes <= 96);
let random_bytes: Vec<u8> = (0..num_bytes).map(|_| thread_rng().gen::<u8>()).collect();
let code_verifier = PkceCodeVerifier::new(base64::encode_config(
&random_bytes,
base64::URL_SAFE_NO_PAD,
));
(
Self::from_code_verifier_sha256(&code_verifier),
code_verifier,
)
}
pub fn from_code_verifier_sha256(code_verifier: &PkceCodeVerifier) -> Self {
assert!(code_verifier.secret().len() >= 43 && code_verifier.secret().len() <= 128);
let digest = Sha256::digest(code_verifier.secret().as_bytes());
let code_challenge = base64::encode_config(&digest, base64::URL_SAFE_NO_PAD);
Self {
code_challenge,
code_challenge_method: PkceCodeChallengeMethod::new("S256".to_string()),
}
}
pub fn as_str(&self) -> &str {
&self.code_challenge
}
pub fn method(&self) -> &PkceCodeChallengeMethod {
&self.code_challenge_method
}
}
new_secret_type![
#[derive(Clone, Deserialize, Serialize)]
ClientSecret(String)
];
new_secret_type![
#[must_use]
#[derive(Clone, Deserialize, Serialize)]
CsrfToken(String)
impl {
pub fn new_random() -> Self {
CsrfToken::new_random_len(16)
}
pub fn new_random_len(num_bytes: u32) -> Self {
let random_bytes: Vec<u8> = (0..num_bytes).map(|_| thread_rng().gen::<u8>()).collect();
CsrfToken::new(base64::encode_config(&random_bytes, base64::URL_SAFE_NO_PAD))
}
}
];
new_secret_type![
#[derive(Clone, Deserialize, Serialize)]
AuthorizationCode(String)
];
new_secret_type![
#[derive(Clone, Deserialize, Serialize)]
RefreshToken(String)
];
new_secret_type![
#[derive(Clone, Deserialize, Serialize)]
AccessToken(String)
];
new_secret_type![
#[derive(Clone)]
ResourceOwnerPassword(String)
];