commit 7adc045b806960f0f8f3eb2968672eabed586620
parent 19754c967fec78d1e303359c634b41bcf6dce076
Author: Daniel GarcĂa <dani-garcia@users.noreply.github.com>
Date: Sun, 9 Dec 2018 17:58:38 +0100
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.
Diffstat:
4 files changed, 70 insertions(+), 51 deletions(-)
diff --git a/src/api/identity.rs b/src/api/identity.rs
@@ -1,5 +1,3 @@
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
-
use rocket::request::LenientForm;
use rocket::Route;
@@ -15,6 +13,8 @@ use crate::util::{self, JsonMap};
use crate::api::{ApiResult, EmptyResult, JsonResult};
+use crate::auth::ClientIp;
+
use crate::CONFIG;
pub fn routes() -> Vec<Route> {
@@ -22,13 +22,13 @@ pub fn routes() -> Vec<Route> {
}
#[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();
validate_data(&data)?;
match data.grant_type {
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,
"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 {
- // Get the ip for error reporting
- let ip = match remote {
- Some(ip) => ip.ip(),
- None => IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
- };
-
+fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult {
// Validate scope
let scope = data.scope.as_ref().unwrap();
if scope != "api offline_access" {
@@ -79,7 +73,7 @@ fn _password_login(data: ConnectData, conn: DbConn, remote: Option<SocketAddr>)
Some(user) => user,
None => err!(format!(
"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) {
err!(format!(
"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
let mut device = match Device::find_by_uuid(&device_id, &conn) {
Some(device) => {
- // Check if valid device
+ // Check if owned device, and recreate if not
if device.user_uuid != user.uuid {
- match device.delete(&conn) {
- Ok(()) => Device::new(device_id, user.uuid.clone(), device_name, device_type),
- Err(_) => err!("Tried to delete device not owned by user, but failed"),
- }
+ info!("Device exists but is owned by another user. The old device will be discarded");
+ Device::new(device_id, user.uuid.clone(), device_name, device_type)
} else {
device
}
}
- None => {
- // Create new device
- Device::new(device_id, user.uuid.clone(), device_name, device_type)
- }
+ None => Device::new(device_id, user.uuid.clone(), device_name, device_type)
};
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 (access_token, expires_in) = device.refresh_tokens(&user, orgs);
- if device.save(&conn).is_err() {
- err!("Failed to add device to user")
+ if let Err(e) = device.save(&conn) {
+ err!("Failed to add device to user", e)
}
let mut result = json!({
diff --git a/src/auth.rs b/src/auth.rs
@@ -1,7 +1,6 @@
///
/// JWT Handling
///
-
use crate::util::read_file;
use chrono::Duration;
@@ -76,6 +75,7 @@ pub struct JWTClaims {
pub orgowner: Vec<String>,
pub orgadmin: Vec<String>,
pub orguser: Vec<String>,
+ pub orgmanager: Vec<String>,
// user security_stamp
pub sstamp: String,
@@ -90,7 +90,6 @@ pub struct JWTClaims {
///
/// Bearer token authentication
///
-
use rocket::Outcome;
use rocket::request::{self, Request, FromRequest};
@@ -139,13 +138,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers {
// Get access_token
let access_token: &str = match request.headers().get_one("Authorization") {
- Some(a) => {
- match a.rsplit("Bearer ").next() {
- Some(split) => split,
- None => err_handler!("No access token provided")
- }
- }
- None => err_handler!("No access token provided")
+ Some(a) => match a.rsplit("Bearer ").next() {
+ Some(split) => split,
+ 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
@@ -256,7 +253,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for AdminHeaders {
Outcome::Failure(f) => Outcome::Failure(f),
Outcome::Success(headers) => {
if headers.org_user_type >= UserOrgType::Admin {
- Outcome::Success(Self{
+ Outcome::Success(Self {
host: headers.host,
device: headers.device,
user: headers.user,
@@ -285,7 +282,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for OwnerHeaders {
Outcome::Failure(f) => Outcome::Failure(f),
Outcome::Success(headers) => {
if headers.org_user_type == UserOrgType::Owner {
- Outcome::Success(Self{
+ Outcome::Success(Self {
host: headers.host,
device: headers.device,
user: headers.user,
@@ -296,4 +293,26 @@ impl<'a, 'r> FromRequest<'a, 'r> for OwnerHeaders {
}
}
}
-}
-\ No newline at end of file
+}
+
+///
+/// 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 })
+ }
+}
diff --git a/src/db/models/device.rs b/src/db/models/device.rs
@@ -74,6 +74,7 @@ impl Device {
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 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
@@ -92,6 +93,7 @@ impl Device {
orgowner,
orgadmin,
orguser,
+ orgmanager,
sstamp: user.security_stamp.to_string(),
device: self.uuid.to_string(),
diff --git a/src/util.rs b/src/util.rs
@@ -2,22 +2,32 @@
/// Macros
///
#[macro_export]
+macro_rules! _err_object {
+ ($msg:expr) => {{
+ err_json!(json!({
+ "Message": "",
+ "error": "",
+ "error_description": "",
+ "ValidationErrors": {"": [ $msg ]},
+ "ErrorModel": {
+ "Message": $msg,
+ "Object": "error"
+ },
+ "Object": "error"
+ }))
+ }};
+}
+
+#[macro_export]
macro_rules! err {
- ($err:expr, $msg:expr) => {{
+ ($msg:expr) => {{
error!("{}", $msg);
- err_json!(json!({
- "error": $err,
- "error_description": $err,
- "ErrorModel": {
- "Message": $msg,
- "ValidationErrors": null,
- "ExceptionMessage": null,
- "ExceptionStackTrace": null,
- "InnerExceptionMessage": null,
- "Object": "error"
- }}))
+ _err_object!($msg)
}};
- ($msg:expr) => { err!("unknown_error", $msg) }
+ ($usr_msg:expr, $log_value:expr) => {{
+ error!("{}: {:#?}", $usr_msg, $log_value);
+ _err_object!($usr_msg)
+ }}
}
#[macro_export]