veloren_server/
login_provider.rs1use crate::{
2 Client,
3 settings::{AdminRecord, Ban, Banlist, WhitelistRecord, banlist::NormalizedIpAddr},
4};
5use authc::{AuthClient, AuthClientError, AuthToken, Uuid};
6use chrono::Utc;
7use common::comp::AdminRole;
8use common_net::msg::RegisterError;
9use hashbrown::HashMap;
10use specs::Component;
11use std::{str::FromStr, sync::Arc};
12use tokio::{runtime::Runtime, sync::oneshot};
13use tracing::{error, info};
14
15pub fn ban_applies(
18 ban: &Ban,
19 admin: Option<&AdminRecord>,
20 now: chrono::DateTime<chrono::Utc>,
21) -> bool {
22 let exceeds_ban_role = |admin: &AdminRecord| {
28 AdminRole::from(admin.role) >= AdminRole::from(ban.performed_by_role())
29 };
30 !ban.is_expired(now) && !admin.is_some_and(exceeds_ban_role)
31}
32
33fn derive_uuid(username: &str) -> Uuid {
34 let mut state = 144066263297769815596495629667062367629;
35
36 for byte in username.as_bytes() {
37 state ^= *byte as u128;
38 state = state.wrapping_mul(309485009821345068724781371);
39 }
40
41 Uuid::from_u128(state)
42}
43
44pub fn derive_singleplayer_uuid() -> Uuid { derive_uuid("singleplayer") }
46
47pub struct PendingLogin {
48 pending_r: oneshot::Receiver<Result<(String, Uuid), RegisterError>>,
49}
50
51impl PendingLogin {
52 pub(crate) fn new_success(username: String, uuid: Uuid) -> Self {
53 let (pending_s, pending_r) = oneshot::channel();
54 let _ = pending_s.send(Ok((username, uuid)));
55
56 Self { pending_r }
57 }
58}
59
60impl Component for PendingLogin {
61 type Storage = specs::DenseVecStorage<Self>;
62}
63
64pub struct LoginProvider {
65 runtime: Arc<Runtime>,
66 auth_server: Option<Arc<AuthClient>>,
67}
68
69impl LoginProvider {
70 pub fn new(auth_addr: Option<String>, runtime: Arc<Runtime>) -> Self {
71 tracing::trace!(?auth_addr, "Starting LoginProvider");
72
73 let auth_server = auth_addr.map(|addr| {
74 let (scheme, authority) = addr.split_once("://").expect("invalid auth url");
75
76 let scheme = scheme
77 .parse::<authc::Scheme>()
78 .expect("invalid auth url scheme");
79 let authority = authority
80 .parse::<authc::Authority>()
81 .expect("invalid auth url authority");
82
83 Arc::new(AuthClient::new(scheme, authority).expect("insecure auth scheme"))
84 });
85
86 Self {
87 runtime,
88 auth_server,
89 }
90 }
91
92 pub fn verify(&self, username_or_token: &str) -> PendingLogin {
93 let (pending_s, pending_r) = oneshot::channel();
94
95 match &self.auth_server {
96 Some(srv) => {
98 let srv = Arc::clone(srv);
99 let username_or_token = username_or_token.to_string();
100 self.runtime.spawn(async move {
101 let _ = pending_s.send(Self::query(srv, &username_or_token).await);
102 });
103 },
104 None => {
106 let username = username_or_token;
107 let uuid = derive_uuid(username);
108 let _ = pending_s.send(Ok((username.to_string(), uuid)));
109 },
110 }
111
112 PendingLogin { pending_r }
113 }
114
115 pub(crate) fn login<R>(
116 pending: &mut PendingLogin,
117 client: &Client,
118 admins: &HashMap<Uuid, AdminRecord>,
119 whitelist: &HashMap<Uuid, WhitelistRecord>,
120 banlist: &Banlist,
121 player_count_exceeded: impl FnOnce(String, Uuid) -> (bool, R),
122 make_ip_ban_upgrade: impl FnOnce(NormalizedIpAddr, Uuid, String),
123 ) -> Option<Result<R, RegisterError>> {
124 match pending.pending_r.try_recv() {
125 Ok(Err(e)) => Some(Err(e)),
126 Ok(Ok((username, uuid))) => {
127 let now = Utc::now();
128 let ip = client
131 .connected_from_addr()
132 .socket_addr()
133 .map(|s| s.ip())
134 .map(NormalizedIpAddr::from);
135 let admin = admins.get(&uuid);
137 if let Some(ban) = banlist
138 .uuid_bans()
139 .get(&uuid)
140 .and_then(|ban_entry| ban_entry.current.action.ban())
141 .into_iter()
142 .chain(ip.and_then(|ip| {
143 banlist
144 .ip_bans()
145 .get(&ip)
146 .and_then(|ban_entry| ban_entry.current.action.ban())
147 }))
148 .find(|ban| ban_applies(ban, admin, now))
149 {
150 if let Some(ip) = ip
151 && ban.upgrade_to_ip
152 {
153 make_ip_ban_upgrade(ip, uuid, username.clone());
154 }
155
156 return Some(Err(RegisterError::Banned(ban.info())));
158 }
159
160 if admin.is_none() && !whitelist.is_empty() && !whitelist.contains_key(&uuid) {
163 return Some(Err(RegisterError::NotOnWhitelist));
164 }
165
166 let (player_count_exceeded, res) = player_count_exceeded(username, uuid);
168 if admin.is_none() && player_count_exceeded {
169 return Some(Err(RegisterError::TooManyPlayers));
170 }
171
172 Some(Ok(res))
173 },
174 Err(oneshot::error::TryRecvError::Closed) => {
175 error!("channel got closed to early, this shouldn't happen");
176 Some(Err(RegisterError::AuthError(
177 "Internal Error verifying".to_string(),
178 )))
179 },
180 Err(oneshot::error::TryRecvError::Empty) => None,
181 }
182 }
183
184 async fn query(
185 srv: Arc<AuthClient>,
186 username_or_token: &str,
187 ) -> Result<(String, Uuid), RegisterError> {
188 info!(?username_or_token, "Validating token");
189 let token = AuthToken::from_str(username_or_token)
191 .map_err(|e| RegisterError::AuthError(e.to_string()))?;
192 match async {
194 let uuid = srv.validate(token).await?;
195 let username = srv.uuid_to_username(uuid).await?;
196 let r: Result<_, AuthClientError> = Ok((username, uuid));
197 r
198 }
199 .await
200 {
201 Err(e) => Err(RegisterError::AuthError(e.to_string())),
202 Ok((username, uuid)) => Ok((username, uuid)),
203 }
204 }
205
206 pub fn username_to_uuid(&self, username: &str) -> Result<Uuid, AuthClientError> {
207 match &self.auth_server {
208 Some(srv) => {
209 self.runtime.block_on(srv.username_to_uuid(&username))
211 },
212 None => Ok(derive_uuid(username)),
213 }
214 }
215
216 pub fn uuid_to_username(
217 &self,
218 uuid: Uuid,
219 fallback_alias: &str,
220 ) -> Result<String, AuthClientError> {
221 match &self.auth_server {
222 Some(srv) => {
223 self.runtime.block_on(srv.uuid_to_username(uuid))
225 },
226 None => Ok(fallback_alias.into()),
227 }
228 }
229}