1
0
Fork 0

Updated IP logging to use client_ip, to match old remote behavior.

Improved error logging, now it won't show a generic error message in some situations.
Removed delete device, which is not needed as it will be overwritten later.
Logged more info when an error occurs saving a device.
Added orgmanager to JWT claims.
Dieser Commit ist enthalten in:
Daniel García 2018-12-09 17:58:38 +01:00
Ursprung 19754c967f
Commit 7adc045b80
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: FC8A7D14C3CD543A
4 geänderte Dateien mit 71 neuen und 51 gelöschten Zeilen

Datei anzeigen

@ -1,5 +1,3 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use rocket::request::LenientForm; use rocket::request::LenientForm;
use rocket::Route; use rocket::Route;
@ -15,6 +13,8 @@ use crate::util::{self, JsonMap};
use crate::api::{ApiResult, EmptyResult, JsonResult}; use crate::api::{ApiResult, EmptyResult, JsonResult};
use crate::auth::ClientIp;
use crate::CONFIG; use crate::CONFIG;
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
@ -22,13 +22,13 @@ pub fn routes() -> Vec<Route> {
} }
#[post("/connect/token", data = "<data>")] #[post("/connect/token", data = "<data>")]
fn login(data: LenientForm<ConnectData>, conn: DbConn, socket: Option<SocketAddr>) -> JsonResult { fn login(data: LenientForm<ConnectData>, conn: DbConn, ip: ClientIp) -> JsonResult {
let data: ConnectData = data.into_inner(); let data: ConnectData = data.into_inner();
validate_data(&data)?; validate_data(&data)?;
match data.grant_type { match data.grant_type {
GrantType::refresh_token => _refresh_login(data, conn), GrantType::refresh_token => _refresh_login(data, conn),
GrantType::password => _password_login(data, conn, socket), GrantType::password => _password_login(data, conn, ip),
} }
} }
@ -56,17 +56,11 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
"Key": user.key, "Key": user.key,
"PrivateKey": user.private_key, "PrivateKey": user.private_key,
}))), }))),
Err(_) => err!("Failed to add device to user"), Err(e) => err!("Failed to add device to user", e),
} }
} }
fn _password_login(data: ConnectData, conn: DbConn, remote: Option<SocketAddr>) -> JsonResult { fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult {
// Get the ip for error reporting
let ip = match remote {
Some(ip) => ip.ip(),
None => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
};
// Validate scope // Validate scope
let scope = data.scope.as_ref().unwrap(); let scope = data.scope.as_ref().unwrap();
if scope != "api offline_access" { if scope != "api offline_access" {
@ -79,7 +73,7 @@ fn _password_login(data: ConnectData, conn: DbConn, remote: Option<SocketAddr>)
Some(user) => user, Some(user) => user,
None => err!(format!( None => err!(format!(
"Username or password is incorrect. Try again. IP: {}. Username: {}.", "Username or password is incorrect. Try again. IP: {}. Username: {}.",
ip, username ip.ip, username
)), )),
}; };
@ -88,7 +82,7 @@ fn _password_login(data: ConnectData, conn: DbConn, remote: Option<SocketAddr>)
if !user.check_valid_password(password) { if !user.check_valid_password(password) {
err!(format!( err!(format!(
"Username or password is incorrect. Try again. IP: {}. Username: {}.", "Username or password is incorrect. Try again. IP: {}. Username: {}.",
ip, username ip.ip, username
)) ))
} }
@ -99,20 +93,15 @@ fn _password_login(data: ConnectData, conn: DbConn, remote: Option<SocketAddr>)
// Find device or create new // Find device or create new
let mut device = match Device::find_by_uuid(&device_id, &conn) { let mut device = match Device::find_by_uuid(&device_id, &conn) {
Some(device) => { Some(device) => {
// Check if valid device // Check if owned device, and recreate if not
if device.user_uuid != user.uuid { if device.user_uuid != user.uuid {
match device.delete(&conn) { info!("Device exists but is owned by another user. The old device will be discarded");
Ok(()) => Device::new(device_id, user.uuid.clone(), device_name, device_type), Device::new(device_id, user.uuid.clone(), device_name, device_type)
Err(_) => err!("Tried to delete device not owned by user, but failed"),
}
} else { } else {
device device
} }
} }
None => { None => Device::new(device_id, user.uuid.clone(), device_name, device_type)
// Create new device
Device::new(device_id, user.uuid.clone(), device_name, device_type)
}
}; };
let twofactor_token = twofactor_auth(&user.uuid, &data.clone(), &mut device, &conn)?; let twofactor_token = twofactor_auth(&user.uuid, &data.clone(), &mut device, &conn)?;
@ -122,8 +111,8 @@ fn _password_login(data: ConnectData, conn: DbConn, remote: Option<SocketAddr>)
let orgs = UserOrganization::find_by_user(&user.uuid, &conn); let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
let (access_token, expires_in) = device.refresh_tokens(&user, orgs); let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
if device.save(&conn).is_err() { if let Err(e) = device.save(&conn) {
err!("Failed to add device to user") err!("Failed to add device to user", e)
} }
let mut result = json!({ let mut result = json!({

Datei anzeigen

@ -1,7 +1,6 @@
/// ///
/// JWT Handling /// JWT Handling
/// ///
use crate::util::read_file; use crate::util::read_file;
use chrono::Duration; use chrono::Duration;
@ -76,6 +75,7 @@ pub struct JWTClaims {
pub orgowner: Vec<String>, pub orgowner: Vec<String>,
pub orgadmin: Vec<String>, pub orgadmin: Vec<String>,
pub orguser: Vec<String>, pub orguser: Vec<String>,
pub orgmanager: Vec<String>,
// user security_stamp // user security_stamp
pub sstamp: String, pub sstamp: String,
@ -90,7 +90,6 @@ pub struct JWTClaims {
/// ///
/// Bearer token authentication /// Bearer token authentication
/// ///
use rocket::Outcome; use rocket::Outcome;
use rocket::request::{self, Request, FromRequest}; use rocket::request::{self, Request, FromRequest};
@ -139,13 +138,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers {
// Get access_token // Get access_token
let access_token: &str = match request.headers().get_one("Authorization") { let access_token: &str = match request.headers().get_one("Authorization") {
Some(a) => { Some(a) => match a.rsplit("Bearer ").next() {
match a.rsplit("Bearer ").next() { Some(split) => split,
Some(split) => split, None => err_handler!("No access token provided"),
None => err_handler!("No access token provided") },
} None => err_handler!("No access token provided"),
}
None => err_handler!("No access token provided")
}; };
// Check JWT token is valid and get device and user from it // Check JWT token is valid and get device and user from it
@ -256,7 +253,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for AdminHeaders {
Outcome::Failure(f) => Outcome::Failure(f), Outcome::Failure(f) => Outcome::Failure(f),
Outcome::Success(headers) => { Outcome::Success(headers) => {
if headers.org_user_type >= UserOrgType::Admin { if headers.org_user_type >= UserOrgType::Admin {
Outcome::Success(Self{ Outcome::Success(Self {
host: headers.host, host: headers.host,
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,
@ -285,7 +282,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for OwnerHeaders {
Outcome::Failure(f) => Outcome::Failure(f), Outcome::Failure(f) => Outcome::Failure(f),
Outcome::Success(headers) => { Outcome::Success(headers) => {
if headers.org_user_type == UserOrgType::Owner { if headers.org_user_type == UserOrgType::Owner {
Outcome::Success(Self{ Outcome::Success(Self {
host: headers.host, host: headers.host,
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,
@ -296,4 +293,26 @@ impl<'a, 'r> FromRequest<'a, 'r> for OwnerHeaders {
} }
} }
} }
} }
///
/// Client IP address detection
///
use std::net::IpAddr;
pub struct ClientIp {
pub ip: IpAddr,
}
impl<'a, 'r> FromRequest<'a, 'r> for ClientIp {
type Error = ();
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
let ip = match request.client_ip() {
Some(addr) => addr,
None => "0.0.0.0".parse().unwrap(),
};
Outcome::Success(ClientIp { ip })
}
}

Datei anzeigen

@ -74,6 +74,7 @@ impl Device {
let orgowner: Vec<_> = orgs.iter().filter(|o| o.type_ == 0).map(|o| o.org_uuid.clone()).collect(); let orgowner: Vec<_> = orgs.iter().filter(|o| o.type_ == 0).map(|o| o.org_uuid.clone()).collect();
let orgadmin: Vec<_> = orgs.iter().filter(|o| o.type_ == 1).map(|o| o.org_uuid.clone()).collect(); let orgadmin: Vec<_> = orgs.iter().filter(|o| o.type_ == 1).map(|o| o.org_uuid.clone()).collect();
let orguser: Vec<_> = orgs.iter().filter(|o| o.type_ == 2).map(|o| o.org_uuid.clone()).collect(); let orguser: Vec<_> = orgs.iter().filter(|o| o.type_ == 2).map(|o| o.org_uuid.clone()).collect();
let orgmanager: Vec<_> = orgs.iter().filter(|o| o.type_ == 3).map(|o| o.org_uuid.clone()).collect();
// Create the JWT claims struct, to send to the client // Create the JWT claims struct, to send to the client
@ -92,6 +93,7 @@ impl Device {
orgowner, orgowner,
orgadmin, orgadmin,
orguser, orguser,
orgmanager,
sstamp: user.security_stamp.to_string(), sstamp: user.security_stamp.to_string(),
device: self.uuid.to_string(), device: self.uuid.to_string(),

Datei anzeigen

@ -2,22 +2,32 @@
/// Macros /// Macros
/// ///
#[macro_export] #[macro_export]
macro_rules! err { macro_rules! _err_object {
($err:expr, $msg:expr) => {{ ($msg:expr) => {{
error!("{}", $msg);
err_json!(json!({ err_json!(json!({
"error": $err, "Message": "",
"error_description": $err, "error": "",
"ErrorModel": { "error_description": "",
"Message": $msg, "ValidationErrors": {"": [ $msg ]},
"ValidationErrors": null, "ErrorModel": {
"ExceptionMessage": null, "Message": $msg,
"ExceptionStackTrace": null, "Object": "error"
"InnerExceptionMessage": null, },
"Object": "error" "Object": "error"
}})) }))
}}; }};
($msg:expr) => { err!("unknown_error", $msg) } }
#[macro_export]
macro_rules! err {
($msg:expr) => {{
error!("{}", $msg);
_err_object!($msg)
}};
($usr_msg:expr, $log_value:expr) => {{
error!("{}: {:#?}", $usr_msg, $log_value);
_err_object!($usr_msg)
}}
} }
#[macro_export] #[macro_export]