Skip to content

Commit

Permalink
minor ui changes
Browse files Browse the repository at this point in the history
  • Loading branch information
bhatti committed Nov 28, 2023
1 parent dfa6a28 commit bd35c38
Show file tree
Hide file tree
Showing 14 changed files with 42 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# PlexPass
A Secured Family-friendly Password Manager
A Secured Family-friendly Password Manager with Multi-factor Authentication and Local Hosting.

## Background
With the proliferation of online services and accounts, it has become almost impossible for users to remember unique and strong passwords for each of them. Some users use the same password across multiple accounts, which is risky because if one account is compromised, all other accounts are at risk. With increase of cyber threats such as [2022-Morgan-Stanley](https://techcrunch.com/2022/09/21/morgan-stanley-hard-drives-data-breach/), [2019-Facebook](https://www.wired.com/story/facebook-passwords-plaintext-change-yours/), [2018-MyFitnessPal](https://www.aafp.org/news/practice-professional-issues/20180403myfitnesspal.html), [2019-CapitalOne](https://www.capitalone.com/digital/facts2019/), more services demand stronger and more complex passwords, which are harder to remember. Standards like [FIDO](https://fidoalliance.org/what-is-fido/) (Fast IDentity Online), [WebAuthn](https://webauthn.guide/) (Web Authentication), and [Passkeys](https://fidoalliance.org/passkeys/) aim to address the problems associated with traditional passwords by introducing stronger, simpler, and more phishing-resistant user authentication methods. These standards mitigate Man-in-the-Middle attacks by using decentralized on-device authentication. Yet, their universal adoption remains a work in progress. Until then, a popular alternative for dealing with the password complexity is a Password manager such as [LessPass](https://www.lesspass.com/#/), [1Password](https://1password.com/), and [Bitwarden](https://bitwarden.com/), which offer enhanced security, convenience, and cross-platform access. However, these password managers are also prone to security and privacy risks especially and become a single point of failure when they store user passwords in the cloud. As password managers may also store other sensitive information such as credit card details and secured notes, the Cloud-based password managers with centralized storage become high value target hackers. Many cloud-based password managers implement additional security measures such as end-to-end encryption, zero-knowledge architecture, and multifactor authentication but once hackers get access to the encrypted password vaults, they become vulnerable to sophisticated encryption attacks. For example, In 2022, LastPass, serving 25 million users, experienced [significant security breaches](https://blog.lastpass.com/2023/03/security-incident-update-recommended-actions/). Attackers accessed a range of user data, including billing and email addresses, names, telephone numbers, and IP addresses. More alarmingly, the breach compromised customer vault data, revealing unencrypted website URLs alongside encrypted usernames, passwords, secure notes, and form-filled information. The access to the encrypted vaults allow [“offline attacks” for password cracking](https://krebsonsecurity.com/2023/09/lastpass-horse-gone-barn-bolted-is-strong-password/) attempts that may use powerful computers for trying millions of password guesses per second. In another incident, LastPass users were [locked out of their accounts due to MFA reset](https://www.bleepingcomputer.com/news/security/lastpass-users-furious-after-being-locked-out-due-to-mfa-resets/) after a security upgrade. In order to address these risks with cloud-based password managers, we are building a secured family-friendly password manager named “PlexPass” with an enhanced security and ease of use including multi-device support for family members but without relying on storing data in cloud.
Expand Down
20 changes: 10 additions & 10 deletions assets/javascript/plexpass.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async function viewAccount(id) {
}
advisories += `</ul>`;
}
const riskBackgroundColor = account.risk_bg_color;
const riskImage = account.risk_image ? `<img width="32" height="32" src="${account.risk_image}">` : '';

modalBody.innerHTML = `
<table class="table table-striped-columns">
Expand All @@ -54,7 +54,7 @@ async function viewAccount(id) {
&nbsp;
<button id="viewPasswordButton" class="btn btn-outline-info" onclick="togglePasswordVisibility()">Show</button>
&nbsp;
<button class="btn btn-outline-dark" onclick="copyToClipboard('${account.password}')">Copy</button>
<button class="btn btn-outline-warning" onclick="copyToClipboard('${account.password}')">Copy</button>
</td>
</tr>
<tr>
Expand All @@ -80,7 +80,7 @@ async function viewAccount(id) {
<td><strong>Notes:</strong></td><td> <span id="viewNotes">${account.notes || ''}</span></td>
</tr>
<tr>
<td><strong>Account Risk:</strong></td><td style="${riskBackgroundColor}"> <span id="viewNotes">${account.risk}</span></td>
<td><strong>Account Risk:</strong></td><td>${riskImage}&nbsp;<span id="viewNotes">${account.risk}</span></td>
</tr>
</table>
<h5>Advisories:</h5>
Expand Down Expand Up @@ -136,7 +136,7 @@ function buildOtpSection(otp, generatedOtp) {
</div>
&nbsp;
<div class="col-auto">
<button class="btn btn-outline-dark" onclick="copyOtpToClipboard()">Copy</button>
<button class="btn btn-outline-warning" onclick="copyOtpToClipboard()">Copy</button>
</div>
</td>
</tr>
Expand Down Expand Up @@ -296,6 +296,12 @@ async function showAccountForm(account) {
<label for="description" class="form-label">Description:</label>
<input type="text" class="form-control" id="description" name="description" value="${account.description || ''}">
</div>
<div class="form-group mb-3">
<label for="editCategory" class="form-label">Category: </label>
<select class="form-select" id="editCategory" name="category">
${category_opts}
</select>
</div>
<div class="form-group mb-3">
<label for="username" class="form-label">Username:</label>
<input type="text" class="form-control" id="username" name="username" value="${account.username || ''}">
Expand All @@ -320,12 +326,6 @@ async function showAccountForm(account) {
<label for="website_url" class="form-label">Website URL:</label>
<input type="url" class="form-control" id="website_url" name="website_url" value="${account.website_url || ''}">
</div>
<div class="form-group mb-3">
<label for="editCategory" class="form-label">Category: </label>
<select class="form-select" id="editCategory" name="category">
${category_opts}
</select>
</div>
<div class="form-group mb-3">
<label>Tags (separated by commas):</label>
<input type="text" class="form-control" name="tags" value="${account.tags || ''}" placeholder="Add tags...">
Expand Down
Binary file modified docs/edit_profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/register_mfa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/view_account.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions resources/en-US/errors.ftl
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
auth-error = We could not validate your credentials, please verify them if you already have an account or Sign up as a new user.
weak-master-password = Your master password with {$info}. Please choose a strong password with a minimum of 12 characters, containing uppercase and lowercase letters, numbers, and special symbols such as `{ $sample_password }`.
master-confirm-mismatch = Your master-password didn't match confirmed master-password, please confirm again.
email-compromise-error = failed to check email for compromise: {$err}.
short-secret-error = secret length {$len} is too small.
user-id-mismatch-error = user_id in context {$id1} didn't match user_id {$id2} in the request.
username-mismatch-error = username in context didn't match target user entity.
acl-admin-only = only admin can update ACL rules.
1 change: 1 addition & 0 deletions resources/en-US/main.ftl
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
welcome = Welcome to PlexPass, your personal Password Manaager!
hello = Hello { $name } to PlexPass the { $place }.
plexpass = PlexPass/1.0
20 changes: 3 additions & 17 deletions src/command/startup_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;

use actix_cors::Cors;
use actix_files::{Files, NamedFile};
use actix_session::config::PersistentSession;
use actix_session::SessionMiddleware;
use actix_session::storage::CookieSessionStore;
use actix_web::{App, http, HttpResponse, HttpServer, middleware, web};
use actix_web::{App, HttpResponse, HttpServer, middleware, web};
use actix_web::cookie::Key;
use actix_web_prom::PrometheusMetricsBuilder;
use openssl::pkey::{PKey, Private};
Expand Down Expand Up @@ -71,21 +70,6 @@ pub async fn execute(config: PassConfig) -> PassResult<()> {
.service(
Files::new("/assets", "./assets")
)
.wrap(
Cors::default() // allowed_origin return access-control-allow-origin: * by default
.allowed_origin(&format!("http://127.0.0.1:{}", http_port.clone()))
.allowed_origin(&format!("https://127.0.0.1:{}", https_port.clone()))
.allowed_origin(&format!("http://localhost:{}", http_port.clone()))
.allowed_origin(&format!("https://localhost:{}", https_port.clone()))
.send_wildcard()
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
.allowed_headers(vec![
http::header::AUTHORIZATION,
http::header::ACCEPT,
http::header::CONTENT_TYPE,
])
.max_age(3600), // for cors
)
.wrap(middleware::Logger::default())
.wrap(auth_middleware::Authentication)
.wrap(prometheus.clone())
Expand All @@ -106,6 +90,8 @@ pub async fn execute(config: PassConfig) -> PassResult<()> {
.configure(config_services)
});

println!("----------session {}", config.session_timeout_minutes);

match load_rustls_config(&config) {
Ok(server_config) => {
log::info!("starting TLS based API server on {}", https_port);
Expand Down
5 changes: 4 additions & 1 deletion src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::domain::models::{
CryptoAlgorithm, DecryptRequest, DecryptResponse, EncryptRequest, EncryptResponse,
HashAlgorithm, PassResult, PBKDF2_HMAC_SHA256_ITERATIONS,
};
use crate::locales::safe_localized_message;

// Cryptographically secure random number generator

Expand Down Expand Up @@ -117,7 +118,9 @@ pub(crate) fn generate_private_public_keys_from_secret(
pub(crate) fn generate_private_key_from_secret(secret: &str) -> PassResult<(SecretKey, [u8; SECRET_LEN])> {
let in_bytes = secret.as_bytes();
if in_bytes.len() < SECRET_LEN {
return Err(PassError::validation(format!("secret ({}:{})is too small", secret, secret.len()).as_str(), None));
return Err(PassError::validation(
&safe_localized_message("short-secret-error", Some(&["len", &secret.len().to_string()])),
None));
}
let mut p = [0; SECRET_LEN];
p[..SECRET_LEN].copy_from_slice(&in_bytes[..SECRET_LEN]);
Expand Down
11 changes: 8 additions & 3 deletions src/dao/acl_repository_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::dao::schema::acls::dsl::*;
use crate::dao::{DbConnection, DbPool, ACLRepository, Repository};
use crate::domain::error::PassError;
use crate::domain::models::{PaginatedResult, PassResult};
use crate::locales::safe_localized_message;

#[derive(Clone)]
pub(crate) struct ACLRepositoryImpl {
Expand Down Expand Up @@ -49,7 +50,7 @@ impl ACLRepositoryImpl {
{
Ok(count) => {
count > 0
},
}
Err(_) => false,
}
}
Expand Down Expand Up @@ -85,7 +86,9 @@ impl Repository<ACLEntity, ACLEntity> for ACLRepositoryImpl {
// create acl.
async fn create(&self, ctx: &UserContext, acl_entity: &ACLEntity) -> PassResult<usize> {
if !ctx.is_admin() {
return Err(PassError::authentication("only admin can create ACL repository"));
return Err(PassError::authentication(
&safe_localized_message("acl-admin-only", None),
));
}
let mut conn = self.connection()?;
let size = Self::create_conn(acl_entity, &mut conn)?;
Expand All @@ -95,7 +98,9 @@ impl Repository<ACLEntity, ACLEntity> for ACLRepositoryImpl {
// updates existing acl.
async fn update(&self, ctx: &UserContext, acl_entity: &ACLEntity) -> PassResult<usize> {
if !ctx.is_admin() {
return Err(PassError::authentication("only admin can update ACL repository"));
return Err(PassError::authentication(
&safe_localized_message("acl-admin-only", None),
));
}

let existing_acl_entity = self.get_entity(ctx, &acl_entity.acl_id).await?;
Expand Down
10 changes: 5 additions & 5 deletions src/dao/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};
use uuid::Uuid;
use crate::locales::safe_localized_message;

pub const CONTEXT_IP_ADDRESS: &str = "ip_address";

Expand Down Expand Up @@ -131,7 +132,7 @@ impl UserContext {
Ok(())
} else {
Err(PassError::authorization(
"username in context didn't match target user entity",
&safe_localized_message("username-mismatch-error", None),
))
}
}
Expand All @@ -144,10 +145,9 @@ impl UserContext {
Ok(())
} else {
eprintln!("backtrace: {}", Backtrace::capture());
Err(PassError::authorization(&format!(
"user_id in context ({}) didn't match user_id ({}) in the request",
&self.user_id, user_id
)))
Err(PassError::authorization(
&safe_localized_message("user-id-mismatch-error", Some(&["id1", &self.user_id, "id2", user_id])),
))
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/domain/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1386,15 +1386,14 @@ impl AccountPasswordSummary {
}

pub const DEFAULT_VAULT_NAMES: [&str; 5] = ["Identity", "Personal", "Work", "Financial", "Secure Notes"];
pub const DEFAULT_CATEGORIES: [&str; 11] = [
pub const DEFAULT_CATEGORIES: [&str; 10] = [
"Contacts",
"Logins",
"Finance",
"Social",
"Shopping",
"Travel",
"Gaming",
"Chat",
"Notes",
"Credit Cards",
"Miscellaneous",
Expand Down
7 changes: 4 additions & 3 deletions src/hibp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::crypto::compute_sha1_hex;
use crate::domain::error::PassError;
use crate::domain::models::PassResult;
use log::info;
use crate::locales::safe_localized_message;

/// email_compromised checks if an account with given email has been compromised
pub(crate) async fn email_compromised(email: &str, api_key: &str) -> PassResult<String> {
Expand All @@ -11,8 +12,8 @@ pub(crate) async fn email_compromised(email: &str, api_key: &str) -> PassResult<
email
);
let client = reqwest::Client::builder()
.user_agent("PlexPass/1.0")
.build()?;
.user_agent(safe_localized_message("plexpass", None))
.build()?;

let response = client
.get(&url)
Expand All @@ -28,7 +29,7 @@ pub(crate) async fn email_compromised(email: &str, api_key: &str) -> PassResult<
info!("-debug hibp---{}", text);
if text.contains("statusCode") || text.contains("hibp-api-key") {
return Err(PassError::runtime(
format!("failed to check email for compromise: {}", text).as_str(),
&safe_localized_message("email-compromise-error", Some(&["err", &text])),
None,
));
}
Expand Down

0 comments on commit bd35c38

Please sign in to comment.