Browse Source

policy enforcement - multiple devices

pull/1955/head
Stuart Heap 4 years ago
parent
commit
284d2155c0
No known key found for this signature in database GPG Key ID: C753450AB379AA25
  1. 967
      Cargo.lock
  2. 81
      src/api/identity.rs
  3. 4
      src/db/models/org_policy.rs

967
Cargo.lock

File diff suppressed because it is too large

81
src/api/identity.rs

@ -51,7 +51,7 @@ fn login(data: Form<ConnectData>, conn: DbConn, ip: ClientIp) -> JsonResult {
_check_is_some(&data.org_identifier, "org_identifier cannot be blank")?;
_check_is_some(&data.device_identifier, "device identifier cannot be blank")?;
_authorization_login(data, conn)
_authorization_login(data, conn, &ip)
}
t => err!("Invalid type", t),
}
@ -87,21 +87,46 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
})))
}
fn _authorization_login(data: ConnectData, conn: DbConn) -> JsonResult {
let (access_token, refresh_token) = get_auth_code_access_token(data.code.unwrap(), data.org_identifier.unwrap(), &conn);
// let expiry = jsonwebtoken::decode_header(access_token.as_str()).unwrap();
let time_now = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs();
#[derive(Debug, Serialize, Deserialize)]
struct TokenPayload {
exp: i64,
email: String,
}
let mut device = Device::find_by_uuid(&data.device_identifier.unwrap(), &conn).map_res("device not found")?;
fn _authorization_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult {
let org_identifier = data.org_identifier.as_ref().unwrap();
let code = data.code.as_ref().unwrap();
let (access_token, refresh_token) = get_auth_code_access_token(&code, &org_identifier, &conn);
let token = jsonwebtoken::dangerous_insecure_decode::<TokenPayload>(access_token.as_str()).unwrap().claims;
let expiry = token.exp;
let user_email = token.email;
let now = Local::now();
// COMMON
let user = User::find_by_uuid(&device.user_uuid, &conn).unwrap();
let user = User::find_by_mail(&user_email, &conn).unwrap();
Ok(Json(json!({
let (mut device, new_device) = get_device(&data, &conn, &user);
let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, ip, &conn)?;
if CONFIG.mail_enabled() && new_device {
if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name) {
error!("Error sending new device email: {:#?}", e);
if CONFIG.require_device_email() {
err!("Could not send login notification email. Please contact your administrator.")
}
}
}
device.refresh_token = refresh_token.clone();
device.save(&conn)?;
let mut result = json!({
"access_token": access_token,
"expires_in": 1000000,
"expires_in": expiry - now.naive_utc().timestamp(),
"token_type": "Bearer",
"refresh_token": device.refresh_token,
"refresh_token": refresh_token,
"Key": user.akey,
"PrivateKey": user.private_key,
@ -110,7 +135,13 @@ fn _authorization_login(data: ConnectData, conn: DbConn) -> JsonResult {
"ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing
"scope": "api offline_access",
"unofficialServer": true,
})))
});
if let Some(token) = twofactor_token {
result["TwoFactorToken"] = Value::String(token);
}
Ok(Json(result))
}
fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult {
@ -138,6 +169,15 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
err!("This user has been disabled", format!("IP: {}. Username: {}.", ip.ip, username))
}
// Check if org policy prevents password login
let user_orgs = UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::RequireSso, &conn);
if user_orgs.len() == 1 && user_orgs[0].atype >= 2 {
// if requires SSO is active, user is in exactly one org by policy rules
// policy only applies to "non-owner/non-admin" members
err!("Organization policy requires SSO sign in");
}
let now = Local::now();
if user.verified_at.is_none() && CONFIG.mail_enabled() && CONFIG.signups_verify() {
@ -532,10 +572,8 @@ fn get_client_from_identifier (identifier: &str, conn: &DbConn) -> CoreClient {
match organization {
Some(organization) => {
println!("found org. authority: {}", organization.authority);
let redirect = organization.callback_path.to_string();
let issuer = reqwest::Url::parse(&organization.authority).unwrap();
println!("got issuer: {}", issuer);
let client_id = ClientId::new(organization.client_id);
let client_secret = ClientSecret::new(organization.client_secret);
let issuer_url = IssuerUrl::new(organization.authority).expect("invalid issuer URL");
@ -564,10 +602,9 @@ fn authorize(
state: &RawStr,
conn: DbConn,
) -> Redirect {
let empty_result = json!({});
let client = get_client_from_identifier(domain_hint.as_str(), &conn);
let (mut authorize_url, csrf_state, _nonce) = client
let (mut authorize_url, _csrf_state, _nonce) = client
.authorize_url(
AuthenticationFlow::<CoreResponseType>::AuthorizationCode,
CsrfToken::new_random,
@ -590,22 +627,17 @@ fn authorize(
let full_query = Vec::from_iter(new_pairs).join("&");
authorize_url.set_query(Some(full_query.as_str()));
// return Redirect::to(rocket::uri!(&authorize_url.to_string()));
return Redirect::to(authorize_url.to_string());
// return Ok(Json(empty_result));
}
fn get_auth_code_access_token (
code: String,
org_identifier: String,
code: &str,
org_identifier: &str,
conn: &DbConn,
) -> (String, String) {
let oidc_code = AuthorizationCode::new(code);
println!("code: {}", oidc_code.secret());
println!("identifier: {}", org_identifier);
let oidc_code = AuthorizationCode::new(String::from(code));
let client = get_client_from_identifier(&org_identifier, conn);
let client = get_client_from_identifier(org_identifier, conn);
let token_response = client
.exchange_code(oidc_code)
@ -618,7 +650,6 @@ fn get_auth_code_access_token (
let access_token = token_response.access_token().secret().to_string();
let refresh_token = token_response.refresh_token().unwrap().secret().to_string();
println!("access token: {}, refresh token: {}", access_token, refresh_token);
(access_token, refresh_token)
}

4
src/db/models/org_policy.rs

@ -27,8 +27,8 @@ pub enum OrgPolicyType {
TwoFactorAuthentication = 0,
MasterPassword = 1,
PasswordGenerator = 2,
// SingleOrg = 3, // Not currently supported.
// RequireSso = 4, // Not currently supported.
SingleOrg = 3,
RequireSso = 4,
PersonalOwnership = 5,
DisableSend = 6,
SendOptions = 7,

Loading…
Cancel
Save