steam_mobile/
user.rs

1//! This module contains the [SteamUser] needed for [crate::SteamAuthenticator] to work.
2
3use std::fmt;
4use std::fmt::Debug;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::path::PathBuf;
8
9use derive_more::Deref;
10use downcast_rs::DowncastSync;
11use steam_totp::Secret;
12
13use crate::errors::AuthError;
14use crate::MobileAuthFile;
15
16/// A steam user needed for the authenticator to work.
17/// Using a MaFile or not will dictate available methods on [`crate::SteamAuthenticator`]
18///
19/// A simple implementation that has everything required to work properly below.
20/// ```no_run
21/// # use steam_mobile::user::SteamUser;
22/// SteamUser::new("test_username".to_string(), "password".to_string())
23///     .parental_code("1111") // Only needed if the is a parental code, otherwise skip
24///     .with_mafile_from_disk("assets/my.maFile")
25///     .expect("Failed to find mafile on disk.");
26/// ```
27#[derive(Clone)]
28pub struct SteamUser<MaFileState> {
29    pub(crate) username: String,
30    pub(crate) password: String,
31    pub(crate) parental_code: Option<String>,
32    mafile: MaFileState,
33}
34
35pub(crate) trait IsUser: DowncastSync {
36    fn username(&self) -> &str;
37    fn password(&self) -> &str;
38}
39downcast_rs::impl_downcast!(sync IsUser);
40
41impl<T> IsUser for SteamUser<T>
42where
43    T: 'static + Send + Sync,
44{
45    fn username(&self) -> &str {
46        &self.username
47    }
48
49    fn password(&self) -> &str {
50        &self.password
51    }
52}
53
54impl<'a: 'static, T> IsUser for &'a SteamUser<T>
55where
56    T: 'static + Send + Sync,
57{
58    fn username(&self) -> &str {
59        &self.username
60    }
61
62    fn password(&self) -> &str {
63        &self.password
64    }
65}
66
67/// State where the user has a MaFile.
68#[derive(Debug, Clone, Deref)]
69pub struct PresentMaFile(MobileAuthFile);
70
71/// State where the user doesn't has a MaFile.
72#[derive(Debug, Copy, Clone)]
73pub struct AbsentMaFile;
74
75impl<T> Display for SteamUser<T> {
76    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
77        write!(f, "Steam User {}", self.username)
78    }
79}
80
81impl<T> Debug for SteamUser<T> {
82    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
83        f.debug_struct("User")
84            .field("username", &self.username)
85            .finish_non_exhaustive()
86    }
87}
88
89impl SteamUser<AbsentMaFile> {
90    /// Creates a new valid `User` with the bare minimum credentials.
91    #[must_use]
92    pub fn new(username: String, password: String) -> SteamUser<AbsentMaFile> {
93        SteamUser {
94            username,
95            password,
96            parental_code: None,
97            mafile: AbsentMaFile,
98        }
99    }
100}
101impl<T> SteamUser<T> {
102    /// Sets the account username, mandatory
103    #[must_use]
104    pub fn username<S>(mut self, username: S) -> Self
105    where
106        S: ToString,
107    {
108        self.username = username.to_string();
109        self
110    }
111
112    /// Sets the account password, mandatory
113    #[must_use]
114    pub fn password<S>(mut self, password: S) -> Self
115    where
116        S: ToString,
117    {
118        self.password = password.to_string();
119        self
120    }
121
122    /// Sets the parental code, if any.
123    #[must_use]
124    pub fn parental_code<S>(mut self, parental_code: S) -> Self
125    where
126        S: ToString,
127    {
128        self.parental_code = Some(parental_code.to_string());
129        self
130    }
131}
132
133impl SteamUser<AbsentMaFile> {
134    /// Convenience function that imports the file from disk
135    pub fn with_mafile_from_disk<P>(self, path: P) -> Result<SteamUser<PresentMaFile>, AuthError>
136    where
137        P: Into<PathBuf>,
138    {
139        Ok(SteamUser {
140            username: self.username,
141            password: self.password,
142            parental_code: self.parental_code,
143            mafile: PresentMaFile(MobileAuthFile::from_disk(path)?),
144        })
145    }
146
147    #[allow(missing_docs)]
148    #[must_use]
149    pub fn with_mafile(self, ma_file: MobileAuthFile) -> SteamUser<PresentMaFile> {
150        SteamUser {
151            username: self.username,
152            password: self.password,
153            parental_code: self.parental_code,
154            mafile: PresentMaFile(ma_file),
155        }
156    }
157}
158
159impl SteamUser<PresentMaFile> {
160    pub(crate) fn shared_secret(&self) -> Secret {
161        Secret::from_b64(&self.mafile.shared_secret).unwrap()
162    }
163
164    pub(crate) fn identity_secret(&self) -> Secret {
165        Secret::from_b64(&self.mafile.identity_secret).unwrap()
166    }
167
168    pub(crate) fn device_id(&self) -> &str {
169        self.mafile.device_id.as_deref().clone().unwrap()
170    }
171}